Class: Array

Inherits:
Object show all
Defined in:
lib/active_support/core_ext/enumerable.rb,
lib/active_support/core_ext/array/wrap.rb,
lib/active_support/core_ext/object/json.rb,
lib/active_support/core_ext/array/access.rb,
lib/active_support/core_ext/object/blank.rb,
lib/active_support/core_ext/array/extract.rb,
lib/active_support/core_ext/array/inquiry.rb,
lib/active_support/core_ext/array/grouping.rb,
lib/active_support/core_ext/object/deep_dup.rb,
lib/active_support/core_ext/object/to_query.rb,
lib/active_support/core_ext/array/conversions.rb,
lib/active_support/core_ext/array/extract_options.rb

Overview

:nodoc:

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 array 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 Array.wrap returns an array with the argument as its single element 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, if the argument does not respond to to_ary it returns an array with the argument as its single element.

The last point is easily explained with some enumerables:

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

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

[*object]

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

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 || [object]
  else
    [object]
  end
end

Instance Method Details

#as_json(options = nil) ⇒ Object

:nodoc:



152
153
154
# File 'lib/active_support/core_ext/object/json.rb', line 152

def as_json(options = nil) #:nodoc:
  map { |v| options ? v.as_json(options.dup) : v.as_json }
end

#deep_dupObject

Returns a deep copy of array.

array = [1, [2, 3]]
dup   = array.deep_dup
dup[1][2] = 4

array[1][2] # => nil
dup[1][2]   # => 4


29
30
31
# File 'lib/active_support/core_ext/object/deep_dup.rb', line 29

def deep_dup
  map(&:deep_dup)
end

#excluding(*elements) ⇒ Object

Returns a copy of the Array excluding the specified elements.

["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
[ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ]) # => [ [ 0, 1 ] ]

Note: This is an optimization of Enumerable#excluding that uses Array#- instead of Array#reject for performance reasons.



47
48
49
# File 'lib/active_support/core_ext/array/access.rb', line 47

def excluding(*elements)
  self - elements.flatten(1)
end

#extract!Object

Removes and returns the elements for which the block returns a true value. If no block is given, an Enumerator is returned instead.

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
odd_numbers = numbers.extract! { |number| number.odd? } # => [1, 3, 5, 7, 9]
numbers # => [0, 2, 4, 6, 8]


10
11
12
13
14
15
16
17
18
19
20
# File 'lib/active_support/core_ext/array/extract.rb', line 10

def extract!
  return to_enum(:extract!) { size } unless block_given?

  extracted_elements = []

  reject! do |element|
    extracted_elements << element if yield(element)
  end

  extracted_elements
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}


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

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

#fifthObject

Equal to self[4].

%w( a b c d e ).fifth # => "e"


80
81
82
# File 'lib/active_support/core_ext/array/access.rb', line 80

def fifth
  self[4]
end

#forty_twoObject

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

(1..42).to_a.forty_two # => 42


87
88
89
# File 'lib/active_support/core_ext/array/access.rb', line 87

def forty_two
  self[41]
end

#fourthObject

Equal to self[3].

%w( a b c d e ).fourth # => "d"


73
74
75
# File 'lib/active_support/core_ext/array/access.rb', line 73

def fourth
  self[3]
end

#from(position) ⇒ Object

Returns the tail of the array from position.

%w( a b c d ).from(0)  # => ["a", "b", "c", "d"]
%w( a b c d ).from(2)  # => ["c", "d"]
%w( a b c d ).from(10) # => []
%w().from(0)           # => []
%w( a b c d ).from(-2) # => ["c", "d"]
%w( a b c ).from(-10)  # => []


12
13
14
# File 'lib/active_support/core_ext/array/access.rb', line 12

def from(position)
  self[position, length] || []
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 8 9 10).in_groups(3, '&nbsp;') {|group| p group}
["1", "2", "3", "4"]
["5", "6", "7", "&nbsp;"]
["8", "9", "10", "&nbsp;"]

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


62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/active_support/core_ext/array/grouping.rb', line 62

def in_groups(number, fill_with = nil)
  # size.div 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.div 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)
    groups << last_group = slice(start, length)
    last_group << fill_with if fill_with != false &&
      modulo > 0 && length == division
    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 8 9 10).in_groups_of(3) {|group| p group}
["1", "2", "3"]
["4", "5", "6"]
["7", "8", "9"]
["10", nil, nil]

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

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


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/active_support/core_ext/array/grouping.rb', line 22

def in_groups_of(number, fill_with = nil)
  if number.to_i <= 0
    raise ArgumentError,
      "Group size must be a positive integer, was #{number.inspect}"
  end

  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(Array.new(padding, fill_with))
  end

  if block_given?
    collection.each_slice(number) { |slice| yield(slice) }
  else
    collection.each_slice(number).to_a
  end
end

#including(*elements) ⇒ Object

Returns a new array that includes the passed elements.

[ 1, 2, 3 ].including(4, 5) # => [ 1, 2, 3, 4, 5 ]
[ [ 0, 1 ] ].including([ [ 1, 0 ] ]) # => [ [ 0, 1 ], [ 1, 0 ] ]


36
37
38
# File 'lib/active_support/core_ext/array/access.rb', line 36

def including(*elements)
  self + elements.flatten(1)
end

#inquiryObject

Wraps the array in an ArrayInquirer object, which gives a friendlier way to check its string-like contents.

pets = [:cat, :dog].inquiry

pets.cat?     # => true
pets.ferret?  # => false

pets.any?(:cat, :ferret)  # => true
pets.any?(:ferret, :alligator)  # => false


16
17
18
# File 'lib/active_support/core_ext/array/inquiry.rb', line 16

def inquiry
  ActiveSupport::ArrayInquirer.new(self)
end

#secondObject

Equal to self[1].

%w( a b c d e ).second # => "b"


59
60
61
# File 'lib/active_support/core_ext/array/access.rb', line 59

def second
  self[1]
end

#second_to_lastObject

Equal to self[-2].

%w( a b c d e ).second_to_last # => "d"


101
102
103
# File 'lib/active_support/core_ext/array/access.rb', line 101

def second_to_last
  self[-2]
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]]


93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/active_support/core_ext/array/grouping.rb', line 93

def split(value = nil)
  arr = dup
  result = []
  if block_given?
    while (idx = arr.index { |i| yield i })
      result << arr.shift(idx)
      arr.shift
    end
  else
    while (idx = arr.index(value))
      result << arr.shift(idx)
      arr.shift
    end
  end
  result << arr
end

#sum(init = nil, &block) ⇒ Object

Array#sum was added in Ruby 2.4 but it only works with Numeric elements.



180
181
182
183
184
185
186
187
# File 'lib/active_support/core_ext/enumerable.rb', line 180

def sum(init = nil, &block)
  if init.is_a?(Numeric) || first.is_a?(Numeric)
    init ||= 0
    orig_sum(init, &block)
  else
    super
  end
end

#thirdObject

Equal to self[2].

%w( a b c d e ).third # => "c"


66
67
68
# File 'lib/active_support/core_ext/array/access.rb', line 66

def third
  self[2]
end

#third_to_lastObject

Equal to self[-3].

%w( a b c d e ).third_to_last # => "c"


94
95
96
# File 'lib/active_support/core_ext/array/access.rb', line 94

def third_to_last
  self[-3]
end

#to(position) ⇒ Object

Returns the beginning of the array up to position.

%w( a b c d ).to(0)  # => ["a"]
%w( a b c d ).to(2)  # => ["a", "b", "c"]
%w( a b c d ).to(10) # => ["a", "b", "c", "d"]
%w().to(0)           # => []
%w( a b c d ).to(-2) # => ["a", "b", "c"]
%w( a b c ).to(-10)  # => []


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

def to(position)
  if position >= 0
    take position + 1
  else
    self[0..position]
  end
end

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

Extends Array#to_s to convert a collection of elements into a comma separated id list if :db argument is given as the format.

Blog.all.to_formatted_s(:db)  # => "1,2,3"
Blog.none.to_formatted_s(:db) # => "null"
[1,2].to_formatted_s          # => "[1, 2]"


93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/active_support/core_ext/array/conversions.rb', line 93

def to_formatted_s(format = :default)
  case format
  when :db
    if empty?
      "null"
    else
      collect(&: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.



42
43
44
# File 'lib/active_support/core_ext/object/to_query.rb', line 42

def to_param
  collect(&: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%5B%5D=Rails&hobbies%5B%5D=coding"


50
51
52
53
54
55
56
57
58
# File 'lib/active_support/core_ext/object/to_query.rb', line 50

def to_query(key)
  prefix = "#{key}[]"

  if empty?
    nil.to_query(prefix)
  else
    collect { |value| value.to_query(prefix) }.join "&"
  end
end

#to_sentence(options = {}) ⇒ Object

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

You can pass the following options to change the default behavior. If you pass an option key that doesn’t exist in the list below, it will raise an ArgumentError.

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 ”).

  • :locale - If i18n is available, you can set a locale and use the connector options defined on the ‘support.array’ namespace in the corresponding dictionary file.

Examples

[].to_sentence                      # => ""
['one'].to_sentence                 # => "one"
['one', 'two'].to_sentence          # => "one and two"
['one', 'two', 'three'].to_sentence # => "one, two, and three"

['one', 'two'].to_sentence(passing: 'invalid option')
# => ArgumentError: Unknown key: :passing. Valid keys are: :words_connector, :two_words_connector, :last_word_connector, :locale

['one', 'two'].to_sentence(two_words_connector: '-')
# => "one-two"

['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ')
# => "one or two or at least three"

Using :locale option:

# Given this locale dictionary:
#
#   es:
#     support:
#       array:
#         words_connector: " o "
#         two_words_connector: " y "
#         last_word_connector: " o al menos "

['uno', 'dos'].to_sentence(locale: :es)
# => "uno y dos"

['uno', 'dos', 'tres'].to_sentence(locale: :es)
# => "uno o dos o al menos tres"


61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/active_support/core_ext/array/conversions.rb', line 61

def to_sentence(options = {})
  options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)

  default_connectors = {
    words_connector: ", ",
    two_words_connector: " and ",
    last_word_connector: ", and "
  }
  if defined?(I18n)
    i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {})
    default_connectors.merge!(i18n_connectors)
  end
  options = default_connectors.merge!(options)

  case length
  when 0
    +""
  when 1
    +"#{self[0]}"
  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 “objects”:

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

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

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>


183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/active_support/core_ext/array/conversions.rb', line 183

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

  options = options.dup
  options[:indent]  ||= 2
  options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
  options[:root]    ||= \
    if first.class != 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" }

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

#without(*elements) ⇒ Object

Alias for #excluding.



52
53
54
# File 'lib/active_support/core_ext/array/access.rb', line 52

def without(*elements)
  excluding(*elements)
end