mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Add Enumerable#index_with.
In the app I'm working on I've wished that index_by had a buddy that would assign the hash value instead of the key multiple times. Enter index_with. Useful when building a hash from a static list of symbols. Before you'd do: ```ruby POST_ATTRIBUTES.map { |attr_name| [ attr_name, public_send(attr_name) ] }.to_h ``` But now that's a little clearer and faster with: ````ruby POST_ATTRIBUTES.index_with { |attr_name| public_send(attr_name) } ``` It's also useful when you have an enumerable that should be converted to a hash, but you don't want to muddle the code up with the overhead that it takes to create that hash. So before, that's: ```ruby WEEKDAYS.each_with_object(Hash.new) do |day, intervals| intervals[day] = [ Interval.all_day ] end ``` And now it's just: ```ruby WEEKDAYS.index_with([ Interval.all_day ]) ``` It's also nice to quickly get a hash with either nil, [], or {} as the value.
This commit is contained in:
parent
ba07b5fc12
commit
39c22303a6
2 changed files with 40 additions and 1 deletions
|
@ -1,6 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Enumerable
|
||||
INDEX_WITH_DEFAULT = Object.new
|
||||
private_constant :INDEX_WITH_DEFAULT
|
||||
|
||||
# Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
|
||||
# when we omit an identity.
|
||||
|
||||
|
@ -37,10 +40,11 @@ module Enumerable
|
|||
end
|
||||
end
|
||||
|
||||
# Convert an enumerable to a hash.
|
||||
# Convert an enumerable to a hash keying it by the block return 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 ...>, ...}
|
||||
def index_by
|
||||
|
@ -53,6 +57,26 @@ module Enumerable
|
|||
end
|
||||
end
|
||||
|
||||
# Convert an enumerable to a hash keying it with the enumerable items and with the values returned in the block.
|
||||
#
|
||||
# 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?" }
|
||||
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
|
||||
|
||||
# Returns +true+ if the enumerable has more than 1 element. Functionally
|
||||
# equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
|
||||
# much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
|
||||
|
|
|
@ -179,6 +179,21 @@ class EnumerableTests < ActiveSupport::TestCase
|
|||
payments.index_by.each(&:price))
|
||||
end
|
||||
|
||||
def test_index_with
|
||||
payments = GenericEnumerable.new([ Payment.new(5), Payment.new(15), Payment.new(10) ])
|
||||
|
||||
assert_equal({ Payment.new(5) => 5, Payment.new(15) => 15, Payment.new(10) => 10 }, payments.index_with(&:price))
|
||||
|
||||
assert_equal({ title: nil, body: nil }, %i( title body ).index_with(nil))
|
||||
assert_equal({ title: [], body: [] }, %i( title body ).index_with([]))
|
||||
assert_equal({ title: {}, body: {} }, %i( title body ).index_with({}))
|
||||
|
||||
assert_equal Enumerator, payments.index_with.class
|
||||
assert_nil payments.index_with.size
|
||||
assert_equal 42, (1..42).index_with.size
|
||||
assert_equal({ Payment.new(5) => 5, Payment.new(15) => 15, Payment.new(10) => 10 }, payments.index_with.each(&:price))
|
||||
end
|
||||
|
||||
def test_many
|
||||
assert_equal false, GenericEnumerable.new([]).many?
|
||||
assert_equal false, GenericEnumerable.new([ 1 ]).many?
|
||||
|
|
Loading…
Reference in a new issue