Module: Enumerable

Includes:
ActiveSupport::EnumerableCoreExt::Constants
Included in:
ActiveSupport::Callbacks::CallbackChain, ActiveSupport::DescendantsTracker::DescendantsArray
Defined in:
lib/active_support/core_ext/enumerable.rb,
lib/active_support/core_ext/object/json.rb

Defined Under Namespace

Classes: SoleItemExpectedError

Instance Method Summary collapse

Instance Method Details

#as_json(options = nil) ⇒ Object

:nodoc:



140
141
142
# File 'lib/active_support/core_ext/object/json.rb', line 140

def as_json(options = nil) # :nodoc:
  to_a.as_json(options)
end

#compact_blankObject

Returns a new Array without the blank items. Uses Object#blank? for determining if an item is blank.

[1, "", nil, 2, " ", [], {}, false, true].compact_blank
# =>  [1, 2, true]

Set.new([nil, "", 1, false]).compact_blank
# => [1]

When called on a Hash, returns a new Hash without the blank values.

{ a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
# => { b: 1, f: true }


235
236
237
# File 'lib/active_support/core_ext/enumerable.rb', line 235

def compact_blank
  reject(&:blank?)
end

#exclude?(object) ⇒ Boolean

The negative of the Enumerable#include?. Returns true if the collection does not include the object.

Returns:

  • (Boolean)


169
170
171
# File 'lib/active_support/core_ext/enumerable.rb', line 169

def exclude?(object)
  !include?(object)
end

#excluding(*elements) ⇒ Object Also known as: without

Returns a copy of the enumerable excluding the specified elements.

["David", "Rafael", "Aaron", "Todd"].excluding "Aaron", "Todd"
# => ["David", "Rafael"]

["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ]
# => ["David", "Rafael"]

{foo: 1, bar: 2, baz: 3}.excluding :bar
# => {foo: 1, baz: 3}


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

def excluding(*elements)
  elements.flatten!(1)
  reject { |element| elements.include?(element) }
end

#in_order_of(key, series) ⇒ Object

Returns a new Array where the order has been set to that provided in the series, based on the key of the objects in the original enumerable.

[ Person.find(5), Person.find(3), Person.find(1) ].in_order_of(:id, [ 1, 5, 3 ])
# => [ Person.find(1), Person.find(5), Person.find(3) ]

If the series include keys that have no corresponding element in the Enumerable, these are ignored. If the Enumerable has additional elements that aren’t named in the series, these are not included in the result.



247
248
249
# File 'lib/active_support/core_ext/enumerable.rb', line 247

def in_order_of(key, series)
  group_by(&key).values_at(*series).flatten.compact
end

#including(*elements) ⇒ Object

Returns a new array that includes the passed elements.

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

["David", "Rafael"].including %w[ Aaron Todd ]
# => ["David", "Rafael", "Aaron", "Todd"]


163
164
165
# File 'lib/active_support/core_ext/enumerable.rb', line 163

def including(*elements)
  to_a.including(*elements)
end

#index_byObject

Convert an enumerable to a hash, using the block result as the key and the element as the value.

people.index_by(&:login)
# => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}

people.index_by { |person| "#{person.first_name} #{person.last_name}" }
# => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}


103
104
105
106
107
108
109
110
111
# File 'lib/active_support/core_ext/enumerable.rb', line 103

def index_by
  if block_given?
    result = {}
    each { |elem| result[yield(elem)] = elem }
    result
  else
    to_enum(:index_by) { size if respond_to?(:size) }
  end
end

#index_with(default = (no_default = true)) ⇒ Object

Convert an enumerable to a hash, using the element as the key and the block result as the value.

post = Post.new(title: "hey there", body: "what's up?")

%i( title body ).index_with { |attr_name| post.public_send(attr_name) }
# => { title: "hey there", body: "what's up?" }

If an argument is passed instead of a block, it will be used as the value for all elements:

%i( created_at updated_at ).index_with(Time.now)
# => { created_at: 2020-03-09 22:31:47, updated_at: 2020-03-09 22:31:47 }


126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/active_support/core_ext/enumerable.rb', line 126

def index_with(default = (no_default = true))
  if block_given?
    result = {}
    each { |elem| result[elem] = yield(elem) }
    result
  elsif no_default
    to_enum(:index_with) { size if respond_to?(:size) }
  else
    result = {}
    each { |elem| result[elem] = default }
    result
  end
end

#many?Boolean

Returns true if the enumerable has more than 1 element. Functionally equivalent to enum.to_a.size > 1. Can be called with a block too, much like any?, so people.many? { |p| p.age > 26 } returns true if more than one person is over 26.

Returns:

  • (Boolean)


144
145
146
147
148
149
150
151
152
153
154
# File 'lib/active_support/core_ext/enumerable.rb', line 144

def many?
  cnt = 0
  if block_given?
    any? do |element, *args|
      cnt += 1 if yield element, *args
      cnt > 1
    end
  else
    any? { (cnt += 1) > 1 }
  end
end

#maximum(key) ⇒ Object

Calculates the maximum from the extracted elements.

payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
payments.maximum(:price) # => 15


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

def maximum(key)
  map(&key).max
end

#minimum(key) ⇒ Object

Calculates the minimum from the extracted elements.

payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
payments.minimum(:price) # => 5


44
45
46
# File 'lib/active_support/core_ext/enumerable.rb', line 44

def minimum(key)
  map(&key).min
end

#pick(*keys) ⇒ Object

Extract the given key from the first element in the enumerable.

[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name)
# => "David"

[{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name)
# => [1, "David"]


212
213
214
215
216
217
218
219
220
# File 'lib/active_support/core_ext/enumerable.rb', line 212

def pick(*keys)
  return if none?

  if keys.many?
    keys.map { |key| first[key] }
  else
    first[keys.first]
  end
end

#pluck(*keys) ⇒ Object

Extract the given key from each element in the enumerable.

[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
# => ["David", "Rafael", "Aaron"]

[{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name)
# => [[1, "David"], [2, "Rafael"]]


196
197
198
199
200
201
202
203
# File 'lib/active_support/core_ext/enumerable.rb', line 196

def pluck(*keys)
  if keys.many?
    map { |element| keys.map { |key| element[key] } }
  else
    key = keys.first
    map { |element| element[key] }
  end
end

#soleObject

Returns the sole item in the enumerable. If there are no items, or more than one item, raises Enumerable::SoleItemExpectedError.

["x"].sole          # => "x"
Set.new.sole        # => Enumerable::SoleItemExpectedError: no item found
{ a: 1, b: 2 }.sole # => Enumerable::SoleItemExpectedError: multiple items found


257
258
259
260
261
262
263
# File 'lib/active_support/core_ext/enumerable.rb', line 257

def sole
  case count
  when 1   then return first # rubocop:disable Style/RedundantReturn
  when 0   then raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "no item found"
  when 2.. then raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "multiple items found"
  end
end

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

Calculates a sum from the elements.

payments.sum { |p| p.price * p.tax_rate }
payments.sum(&:price)

The latter is a shortcut for:

payments.inject(0) { |sum, p| sum + p.price }

It can also calculate the sum without the use of a block.

[5, 15, 10].sum # => 30
['foo', 'bar'].sum('') # => "foobar"
[[1, 2], [3, 1, 5]].sum([]) # => [1, 2, 3, 1, 5]

The default sum of an empty list is zero. You can override this default:

[].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)


74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/active_support/core_ext/enumerable.rb', line 74

def sum(identity = nil, &block)
  if identity
    _original_sum_with_required_identity(identity, &block)
  elsif block_given?
    map(&block).sum
  # we check `first(1) == []` to check if we have an
  # empty Enumerable; checking `empty?` would return
  # true for `[nil]`, which we want to deprecate to
  # keep consistent with Ruby
  elsif first.is_a?(Numeric) || first(1) == [] || first.respond_to?(:coerce)
    identity ||= 0
    _original_sum_with_required_identity(identity, &block)
  else
    ActiveSupport::Deprecation.warn(<<-MSG.squish)
      Rails 7.0 has deprecated Enumerable.sum in favor of Ruby's native implementation available since 2.4.
      Sum of non-numeric elements requires an initial argument.
    MSG
    inject(:+) || 0
  end
end