Feature #10208
closed
Passing block to Enumerable#to_h
Added by yhara (Yutaka HARA) about 10 years ago.
Updated about 6 years ago.
Description
Now that we can convert 'a list of [key, value] pairs' into a hash with Enumerable#to_h,
how about make it take a block to specify 'how to convert each element into a [key, value] pair'?
Example:
# Convert users into an {id => name} hash
users.map{|u| [u.id, u.name]}.to_h
↓
# Convert users into an {id => name} hash
users.to_h{|u| [u.id, u.name]}
This could also be a solution for these feature requests:
-
Feature #6669 A method like Hash#map but returns hash
hsh.apply{|k, v| [k.to_s, v]}
== hsh.to_h{|k, v| [k.to_s, v]}
-
Feature #7793 New methods on Hash
Feature #9970 Add Hash#map_keys
and Hash#map_values
hsh.map_k(&:to_s)
== hsh.to_h{|k, v| [k.to_s, v]}
hsh.map_v(&:to_i)
== hsh.to_h{|k, v| [k, v.to_i]}
hsh.map_kv(&block)
== hsh.to_h(&block)
The name to_h
doesn't feel nice for it, IMHO.
I agree to_h
isn't the right method to do this.
I completely agree that we need new methods to do this.
This would also be a solution for #4151, and the multiple other proposals related to it.
I think the creation of intermediate arrays for each pair is waste of resource. Can we use combination of two methods? For example, something like:
users.with_keys(&:id).with_values(&:name)
or
users.with_values(&:name).with_keys(&:id)
Order of application of the two methods with_keys
and with_values
should not matter. When only one of them has applied, the return value should be something like an enumerator. As soon as both methods have applied, a hash should be returned.
Marc-Andre Lafortune wrote:
I completely agree that we need new methods to do this.
Thanks. One idea is name it Enumerable#hash_by (like max_by, group_by)
User = Struct.new(:id, :name)
users = [User.new(1, "Alice"), User.new(2, "Bob")]
users.hash_by{|u| [u.id, u.name]}
# {1 => "Alice", 2 => "Bob"}
We have #max and #max_by. When we have #hash and #hash_by, people may expect something else.
But it may not matter much. Or maybe we need to rename #hash to #hashcode (off topic here).
Matz.
I like this proposal. I think it is reasonable for to_h
to take an optional block to specify how to make a hash from a given enumerable object because most enumerable objects are not composed of two element arrays. (IIRC that was the reason why Matz didn't like the idea of adding the method when it was first proposed)
The said use case users.map{|u| [u.id, u.name]}.to_h
is only as effective as Hash[users.map{|u| [u.id, u.name]}]
, and that spoils the usefulness of the method defined in Enumerable. users.lazy.map{|u| [u.id, u.name]}.to_h
may be cool in that it does not create an intermediate array object, but it still creates a couple of chained lazy enumerable objects and looks far from being concise and straightforward.
What do you guys think? I don't think we need a new name for this.
We looked at this issue at yesterday's developer meeting and had consensus that there is no other example of to_* method that takes a block. Introducing such new concept seems too risky.
So, we need a different name for this.
Here's some candidates I can think of:
- hash_by (proposed above)
- to_h_by
- hash_map
- map_h
- map_to_h
- Related to Feature #12512: Import Hash#transform_values and its destructive version from ActiveSupport added
- Description updated (diff)
- Status changed from Open to Feedback
FYI if there are people who need this functionality: so far we have not found an appropriately sounding name of this method. It should be a method of Enumerable (not only Hash), that takes a block, and returns a Hash.
So, exactly the same proposal linked above got accepted. The name wasn't actually a problem!
Well, congratulations anyway! 😆
- Status changed from Feedback to Closed
Also available in: Atom
PDF
Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0