Project

General

Profile

Actions

Feature #10208

closed

Passing block to Enumerable#to_h

Added by yhara (Yutaka HARA) over 9 years ago. Updated over 5 years ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-core:64808]

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)
    

Related issues 2 (0 open2 closed)

Related to Ruby master - Feature #12512: Import Hash#transform_values and its destructive version from ActiveSupportClosedmatz (Yukihiro Matsumoto)Actions
Has duplicate Ruby master - Feature #15143: Extend `Enumerable#to_h`Closednobu (Nobuyoshi Nakada)Actions

Updated by nobu (Nobuyoshi Nakada) over 9 years ago

The name to_h doesn't feel nice for it, IMHO.

Updated by matz (Yukihiro Matsumoto) over 9 years ago

I agree with Nobu.

Matz.

Updated by marcandre (Marc-Andre Lafortune) over 9 years ago

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.

Updated by sawa (Tsuyoshi Sawada) over 9 years ago

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.

Updated by yhara (Yutaka HARA) over 9 years ago

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

Updated by matz (Yukihiro Matsumoto) over 9 years ago

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.

Updated by knu (Akinori MUSHA) almost 8 years ago

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.

Updated by shyouhei (Shyouhei Urabe) over 7 years ago

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.

Updated by knu (Akinori MUSHA) over 7 years ago

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
Actions #10

Updated by shyouhei (Shyouhei Urabe) over 7 years ago

  • Related to Feature #12512: Import Hash#transform_values and its destructive version from ActiveSupport added

Updated by shyouhei (Shyouhei Urabe) over 7 years ago

  • Description updated (diff)

Not a direct translation of the proposed method, but Hash#transform_values is now available. https://bugs.ruby-lang.org/issues/12512

Updated by shyouhei (Shyouhei Urabe) over 7 years ago

  • 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.

Actions #13

Updated by nobu (Nobuyoshi Nakada) over 5 years ago

Updated by knu (Akinori MUSHA) over 5 years ago

So, exactly the same proposal linked above got accepted. The name wasn't actually a problem!

Well, congratulations anyway! 😆

Actions #15

Updated by yhara (Yutaka HARA) over 5 years ago

  • Status changed from Feedback to Closed
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0