On this page
module Enumerable
Public Instance Methods
# File activesupport/lib/active_support/core_ext/enumerable.rb, line 218
def compact_blank
reject(&:blank?)
end
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, 2])
# => [2, 1] (or [1, 2])
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 }
# File activesupport/lib/active_support/core_ext/enumerable.rb, line 152
def exclude?(object)
!include?(object)
end
The negative of the Enumerable#include?
. Returns true
if the collection does not include the object.
# File activesupport/lib/active_support/core_ext/enumerable.rb, line 166
def excluding(*elements)
elements.flatten!(1)
reject { |element| elements.include?(element) }
end
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}
# File activesupport/lib/active_support/core_ext/enumerable.rb, line 230
def in_order_of(key, series)
index_by(&key).values_at(*series).compact
end
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.
# File activesupport/lib/active_support/core_ext/enumerable.rb, line 146
def including(*elements)
to_a.including(*elements)
end
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"]
# File activesupport/lib/active_support/core_ext/enumerable.rb, line 86
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
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 ...>, ...}
# File activesupport/lib/active_support/core_ext/enumerable.rb, line 109
def index_with(default = INDEX_WITH_DEFAULT)
if block_given?
result = {}
each { |elem| result[elem] = yield(elem) }
result
elsif default != INDEX_WITH_DEFAULT
result = {}
each { |elem| result[elem] = default }
result
else
to_enum(:index_with) { size if respond_to?(:size) }
end
end
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 }
# File activesupport/lib/active_support/core_ext/enumerable.rb, line 127
def many?
cnt = 0
if block_given?
any? do |element|
cnt += 1 if yield element
cnt > 1
end
else
any? { (cnt += 1) > 1 }
end
end
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.
# File activesupport/lib/active_support/core_ext/enumerable.rb, line 35
def maximum(key)
map(&key).max
end
Calculates the maximum from the extracted elements.
payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
payments.maximum(:price) # => 15
# File activesupport/lib/active_support/core_ext/enumerable.rb, line 27
def minimum(key)
map(&key).min
end
Calculates the minimum from the extracted elements.
payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
payments.minimum(:price) # => 5
# File activesupport/lib/active_support/core_ext/enumerable.rb, line 195
def pick(*keys)
return if none?
if keys.many?
keys.map { |key| first[key] }
else
first[keys.first]
end
end
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"]
# File activesupport/lib/active_support/core_ext/enumerable.rb, line 179
def pluck(*keys)
if keys.many?
map { |element| keys.map { |key| element[key] } }
else
key = keys.first
map { |element| element[key] }
end
end
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"]]
# File activesupport/lib/active_support/core_ext/enumerable.rb, line 240
def sole
case count
when 1 then return first # rubocop:disable Style/RedundantReturn
when 0 then raise SoleItemExpectedError, "no item found"
when 2.. then raise SoleItemExpectedError, "multiple items found"
end
end
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
# File activesupport/lib/active_support/core_ext/enumerable.rb, line 57
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) == []
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
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)
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.