Class: Array

Inherits:
Object show all
Defined in:
lib/active_support/core_ext/object/blank.rb,
lib/active_support/json/encoding.rb,
lib/active_support/core_ext/array/wrap.rb,
lib/active_support/core_ext/array/access.rb,
lib/active_support/core_ext/array/uniq_by.rb,
lib/active_support/core_ext/array/grouping.rb,
lib/active_support/core_ext/object/to_param.rb,
lib/active_support/core_ext/object/to_query.rb,
lib/active_support/core_ext/array/conversions.rb,
lib/active_support/core_ext/array/random_access.rb,
lib/active_support/core_ext/array/extract_options.rb

Overview

:nodoc:

Direct Known Subclasses

ActiveSupport::Callbacks::CallbackChain

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.wrap(object) ⇒ Object

Wraps its argument in an array unless it is already an array (or array-like).

Specifically:

  • If the argument is nil an empty list is returned.

  • Otherwise, if the argument responds to to_ary it is invoked, and its result returned.

  • Otherwise, returns an array with the argument as its single element.

    Array.wrap(nil) # => [] Array.wrap([1, 2, 3]) # => [1, 2, 3] Array.wrap(0) # => [0]

This method is similar in purpose to Kernel#Array, but there are some differences:

  • If the argument responds to to_ary the method is invoked. Kernel#Array

moves on to try to_a if the returned value is nil, but Arraw.wrap returns such a nil right away.

  • If the returned value from to_ary is neither nil nor an Array object, Kernel#Array

raises an exception, while Array.wrap does not, it just returns the value.

  • It does not call to_a on the argument, though special-cases nil to return an empty array.

The last point is particularly worth comparing for some enumerables:

Array(:foo => :bar)      # => [[:foo, :bar]]
Array.wrap(:foo => :bar) # => [{:foo => :bar}]

Array("foo\nbar")        # => ["foo\n", "bar"], in Ruby 1.8
Array.wrap("foo\nbar")   # => ["foo\nbar"]

There’s also a related idiom that uses the splat operator:

[*object]

which returns [nil] for nil, and calls to Array(object) otherwise.

Thus, in this case the behavior is different for nil, and the differences with Kernel#Array explained above apply to the rest of objects.



39
40
41
42
43
44
45
46
47
# File 'lib/active_support/core_ext/array/wrap.rb', line 39

def self.wrap(object)
  if object.nil?
    []
  elsif object.respond_to?(:to_ary)
    object.to_ary
  else
    [object]
  end
end

Instance Method Details

#as_json(options = nil) ⇒ Object

:nodoc:



206
207
208
209
210
# File 'lib/active_support/json/encoding.rb', line 206

def as_json(options = nil) #:nodoc:
  # use encoder as a proxy to call as_json on all elements, to protect from circular references
  encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
  map { |v| encoder.as_json(v) }
end

#encode_json(encoder) ⇒ Object

:nodoc:



212
213
214
215
# File 'lib/active_support/json/encoding.rb', line 212

def encode_json(encoder) #:nodoc:
  # we assume here that the encoder has already run as_json on self and the elements, so we run encode_json directly
  "[#{map { |v| v.encode_json(encoder) } * ','}]"
end

#extract_options!Object

Extracts options from a set of arguments. Removes and returns the last element in the array if it’s a hash, otherwise returns a blank hash.

def options(*args)
  args.extract_options!
end

options(1, 2)           # => {}
options(1, 2, :a => :b) # => {:a=>:b}


22
23
24
25
26
27
28
# File 'lib/active_support/core_ext/array/extract_options.rb', line 22

def extract_options!
  if last.is_a?(Hash) && last.extractable_options?
    pop
  else
    {}
  end
end

#fifthObject

Equal to self[4].



38
39
40
# File 'lib/active_support/core_ext/array/access.rb', line 38

def fifth
  self[4]
end

#forty_twoObject

Equal to self[41]. Also known as accessing “the reddit”.



43
44
45
# File 'lib/active_support/core_ext/array/access.rb', line 43

def forty_two
  self[41]
end

#fourthObject

Equal to self[3].



33
34
35
# File 'lib/active_support/core_ext/array/access.rb', line 33

def fourth
  self[3]
end

#from(position) ⇒ Object

Returns the tail of the array from position.

%w( a b c d ).from(0)  # => %w( a b c d )
%w( a b c d ).from(2)  # => %w( c d )
%w( a b c d ).from(10) # => nil
%w().from(0)           # => %w()


8
9
10
# File 'lib/active_support/core_ext/array/access.rb', line 8

def from(position)
  self[position..-1]
end

#in_groups(number, fill_with = nil) ⇒ Object

Splits or iterates over the array in number of groups, padding any remaining slots with fill_with unless it is false.

%w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group}
["1", "2", "3", "4"]
["5", "6", "7", nil]
["8", "9", "10", nil]

%w(1 2 3 4 5 6 7).in_groups(3, ' ') {|group| p group}
["1", "2", "3"]
["4", "5", " "]
["6", "7", " "]

%w(1 2 3 4 5 6 7).in_groups(3, false) {|group| p group}
["1", "2", "3"]
["4", "5"]
["6", "7"]


56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/active_support/core_ext/array/grouping.rb', line 56

def in_groups(number, fill_with = nil)
  # size / number gives minor group size;
  # size % number gives how many objects need extra accommodation;
  # each group hold either division or division + 1 items.
  division = size / number
  modulo = size % number

  # create a new array avoiding dup
  groups = []
  start = 0

  number.times do |index|
    length = division + (modulo > 0 && modulo > index ? 1 : 0)
    padding = fill_with != false &&
      modulo > 0 && length == division ? 1 : 0
    groups << slice(start, length).concat([fill_with] * padding)
    start += length
  end

  if block_given?
    groups.each { |g| yield(g) }
  else
    groups
  end
end

#in_groups_of(number, fill_with = nil) ⇒ Object

Splits or iterates over the array in groups of size number, padding any remaining slots with fill_with unless it is false.

%w(1 2 3 4 5 6 7).in_groups_of(3) {|group| p group}
["1", "2", "3"]
["4", "5", "6"]
["7", nil, nil]

%w(1 2 3).in_groups_of(2, '&nbsp;') {|group| p group}
["1", "2"]
["3", "&nbsp;"]

%w(1 2 3).in_groups_of(2, false) {|group| p group}
["1", "2"]
["3"]


19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/active_support/core_ext/array/grouping.rb', line 19

def in_groups_of(number, fill_with = nil)
  if fill_with == false
    collection = self
  else
    # size % number gives how many extra we have;
    # subtracting from number gives how many to add;
    # modulo number ensures we don't add group of just fill.
    padding = (number - size % number) % number
    collection = dup.concat([fill_with] * padding)
  end

  if block_given?
    collection.each_slice(number) { |slice| yield(slice) }
  else
    groups = []
    collection.each_slice(number) { |group| groups << group }
    groups
  end
end

#sample(n = nil) ⇒ Object

Backport of Array#sample based on Marc-Andre Lafortune’s github.com/marcandre/backports/ Returns a random element or n random elements from the array. If the array is empty and n is nil, returns nil. if n is passed, returns [].

[1,2,3,4,5,6].sample    # => 4
[1,2,3,4,5,6].sample(3) # => [2, 4, 5]
           [].sample    # => nil
           [].sample(3) # => []


10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/active_support/core_ext/array/random_access.rb', line 10

def sample(n=nil)
  return self[Kernel.rand(size)] if n.nil?
  n = n.to_int
rescue Exception => e
  raise TypeError, "Coercion error: #{n.inspect}.to_int => Integer failed:\n(#{e.message})"
else
  raise TypeError, "Coercion error: obj.to_int did NOT return an Integer (was #{n.class})" unless n.kind_of? Integer
  raise ArgumentError, "negative array size" if n < 0
  n = size if n > size
  result = Array.new(self)
  n.times do |i|
    r = i + Kernel.rand(size - i)
    result[i], result[r] = result[r], result[i]
  end
  result[n..size] = []
  result
end

#secondObject

Equal to self[1].



23
24
25
# File 'lib/active_support/core_ext/array/access.rb', line 23

def second
  self[1]
end

#split(value = nil) ⇒ Object

Divides the array into one or more subarrays based on a delimiting value or the result of an optional block.

[1, 2, 3, 4, 5].split(3)                # => [[1, 2], [4, 5]]
(1..10).to_a.split { |i| i % 3 == 0 }   # => [[1, 2], [4, 5], [7, 8], [10]]


87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/active_support/core_ext/array/grouping.rb', line 87

def split(value = nil)
  using_block = block_given?

  inject([[]]) do |results, element|
    if (using_block && yield(element)) || (value == element)
      results << []
    else
      results.last << element
    end

    results
  end
end

#thirdObject

Equal to self[2].



28
29
30
# File 'lib/active_support/core_ext/array/access.rb', line 28

def third
  self[2]
end

#to(position) ⇒ Object

Returns the beginning of the array up to position.

%w( a b c d ).to(0)  # => %w( a )
%w( a b c d ).to(2)  # => %w( a b c )
%w( a b c d ).to(10) # => %w( a b c d )
%w().to(0)           # => %w()


18
19
20
# File 'lib/active_support/core_ext/array/access.rb', line 18

def to(position)
  self[0..position]
end

#to_formatted_s(format = :default) ⇒ Object Also known as: to_s

Converts a collection of elements into a formatted string by calling to_s on all elements and joining them:

Blog.find(:all).to_formatted_s # => "First PostSecond PostThird Post"

Adding in the :db argument as the format yields a prettier output:

Blog.find(:all).to_formatted_s(:db) # => "First Post,Second Post,Third Post"


46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/active_support/core_ext/array/conversions.rb', line 46

def to_formatted_s(format = :default)
  case format
    when :db
      if respond_to?(:empty?) && self.empty?
        "null"
      else
        collect { |element| element.id }.join(",")
      end
    else
      to_default_s
  end
end

#to_paramObject

Calls to_param on all its elements and joins the result with slashes. This is used by url_for in Action Pack.



31
32
33
# File 'lib/active_support/core_ext/object/to_param.rb', line 31

def to_param
  collect { |e| e.to_param }.join '/'
end

#to_query(key) ⇒ Object

Converts an array into a string suitable for use as a URL query string, using the given key as the param name.

['Rails', 'coding'].to_query('hobbies') # => "hobbies[]=Rails&hobbies[]=coding"


19
20
21
22
# File 'lib/active_support/core_ext/object/to_query.rb', line 19

def to_query(key)
  prefix = "#{key}[]"
  collect { |value| value.to_query(prefix) }.join '&'
end

#to_sentence(options = {}) ⇒ Object

Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options:

  • :words_connector - The sign or word used to join the elements in arrays with two or more elements (default: “, ”)

  • :two_words_connector - The sign or word used to join the elements in arrays with two elements (default: “ and ”)

  • :last_word_connector - The sign or word used to join the last element in arrays with three or more elements (default: “, and ”)



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/active_support/core_ext/array/conversions.rb', line 11

def to_sentence(options = {})
  if defined?(I18n)
    default_words_connector     = I18n.translate(:'support.array.words_connector',     :locale => options[:locale])
    default_two_words_connector = I18n.translate(:'support.array.two_words_connector', :locale => options[:locale])
    default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale])
  else
    default_words_connector     = ", "
    default_two_words_connector = " and "
    default_last_word_connector = ", and "
  end

  options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
  options.reverse_merge! :words_connector => default_words_connector, :two_words_connector => default_two_words_connector, :last_word_connector => default_last_word_connector

  case length
    when 0
      ""
    when 1
      self[0].to_s.dup
    when 2
      "#{self[0]}#{options[:two_words_connector]}#{self[1]}"
    else
      "#{self[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{self[-1]}"
  end
end

#to_xml(options = {}) ⇒ Object

Returns a string that represents the array in XML by invoking to_xml on each element. Active Record collections delegate their representation in XML to this method.

All elements are expected to respond to to_xml, if any of them does not then an exception is raised.

The root node reflects the class name of the first element in plural if all elements belong to the same type and that’s not Hash:

customer.projects.to_xml

<?xml version="1.0" encoding="UTF-8"?>
<projects type="array">
  <project>
    <amount type="decimal">20000.0</amount>
    <customer-id type="integer">1567</customer-id>
    <deal-date type="date">2008-04-09</deal-date>
    ...
  </project>
  <project>
    <amount type="decimal">57230.0</amount>
    <customer-id type="integer">1567</customer-id>
    <deal-date type="date">2008-04-15</deal-date>
    ...
  </project>
</projects>

Otherwise the root element is “records”:

[{:foo => 1, :bar => 2}, {:baz => 3}].to_xml

<?xml version="1.0" encoding="UTF-8"?>
<records type="array">
  <record>
    <bar type="integer">2</bar>
    <foo type="integer">1</foo>
  </record>
  <record>
    <baz type="integer">3</baz>
  </record>
</records>

If the collection is empty the root element is “nil-classes” by default:

[].to_xml

<?xml version="1.0" encoding="UTF-8"?>
<nil-classes type="array"/>

To ensure a meaningful root element use the :root option:

customer_with_no_projects.projects.to_xml(:root => "projects")

<?xml version="1.0" encoding="UTF-8"?>
<projects type="array"/>

By default name of the node for the children of root is root.singularize. You can change it with the :children option.

The options hash is passed downwards:

Message.all.to_xml(:skip_types => true)

<?xml version="1.0" encoding="UTF-8"?>
<messages>
  <message>
    <created-at>2008-03-07T09:58:18+01:00</created-at>
    <id>1</id>
    <name>1</name>
    <updated-at>2008-03-07T09:58:18+01:00</updated-at>
    <user-id>1</user-id>
  </message>
</messages>


136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/active_support/core_ext/array/conversions.rb', line 136

def to_xml(options = {})
  require 'active_support/builder' unless defined?(Builder)

  options = options.dup
  options[:indent]  ||= 2
  options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
  options[:root]    ||= if first.class.to_s != "Hash" && all? { |e| e.is_a?(first.class) }
    underscored = ActiveSupport::Inflector.underscore(first.class.name)
    ActiveSupport::Inflector.pluralize(underscored).tr('/', '_')
  else
    "objects"
  end

  builder = options[:builder]
  builder.instruct! unless options.delete(:skip_instruct)

  root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options)
  children = options.delete(:children) || root.singularize

  attributes = options[:skip_types] ? {} : {:type => "array"}
  return builder.tag!(root, attributes) if empty?

  builder.__send__(:method_missing, root, attributes) do
    each { |value| ActiveSupport::XmlMini.to_tag(children, value, options) }
    yield builder if block_given?
  end
end

#uniq_byObject

Return an unique array based on the criteria given as a proc.

[1, 2, 3, 4].uniq_by { |i| i.odd? }
# => [1, 2]


7
8
9
10
11
# File 'lib/active_support/core_ext/array/uniq_by.rb', line 7

def uniq_by
  hash, array = {}, []
  each { |i| hash[yield(i)] ||= (array << i) }
  array
end

#uniq_by!Object

Same as uniq_by, but modifies self.



14
15
16
# File 'lib/active_support/core_ext/array/uniq_by.rb', line 14

def uniq_by!
  replace(uniq_by{ |i| yield(i) })
end