Issue #7241 has been reported by nathan.f77 (Nathan Broadbent).

----------------------------------------
Feature #7241: Enumerable#to_h proposal
https://bugs.ruby-lang.org/issues/7241

Author: nathan.f77 (Nathan Broadbent)
Status: Open
Priority: Normal
Assignee: 
Category: core
Target version: 


I often use the `inject` method to build a hash, but I always find it annoying when I need to return the hash at the end of the block.
This means that I often write code like:

    [1,2,3,4,5].inject({}) {|hash, el| hash[el] = el * 2; hash }

I'm proposing an `Enumerable#to_h` method that would let me write:

    [1,2,3,4,5].to_h {|h, el| h[el] = el * 2 }


I saw the proposal at http://bugs.ruby-lang.org/issues/666, but I would not be in favor of his implementation.
I believe the implementation should be similar to `inject`, so that the hash object and next element are passed to the block. The main difference to the `inject` method is that we would be modifying the hash in place, instead of relying on the block's return value.

As well as providing support for the case above, I have also considered other cases where the `to_h` method would be useful.
I thought it would be useful if symmetry were provided for the `Hash#to_a` method, such that:

    hash.to_a.to_h == hash  # => true

(See example 2)


I've allowed developers to provide a symbol instead of a block, so that each element in the collection will be passed to that named method. (See example 3)

Finally, hashes can be given a default value, or a Proc that returns the default value. (See examples 4 & 5)


Heres an example implementation that I would be happy to rewrite in C if necessary:


    module Enumerable
      def to_h(default_or_sym = nil)
        if block_given?
          hash = if Proc === default_or_sym
            Hash.new(&default_or_sym)
          else
            Hash.new(default_or_sym)
          end
          self.each do |el|
            yield hash, el
          end
        elsif !default_or_sym.nil?
          hash = {}
          self.each do |el|
            hash[el] = el.send(default_or_sym)
          end
        else
          return Hash[*self.to_a.flatten(1)]
        end
        hash
      end
    end


Examples
----------------------------------------------


# 1) Build a hash from array elements

    [1,2,3,4,5].to_h {|h, el| h[el] = el * 2 }

=> {1=>2, 2=>4, 3=>6, 4=>8, 5=>10}


# 2) Provides symmetry for Hash.to_a (i.e. you can call hash.to_a.to_h)

    [[1, 2], [3, 4], [5, 6]].to_h

=> {1=>2, 3=>4, 5=>6}


# 3) Build a hash by calling a method on each array element

    ["String", "Another String"].to_h(:size)

=> {"String"=>6, "Another String"=>14}


# 4) Hash with default value

    [4,5,6,5].to_h(0) {|h, el| h[el] += el }

=> {4=>4, 5=>10, 6=>6}


# 5) Hash with default value returned from Proc

    default_proc = -> hash, key { hash[key] = "go fish: #{key}" }
    [4,5,6].to_h(default_proc) {|h, el| h[el].upcase! }

=> {4=>"GO FISH: 4", 5=>"GO FISH: 5", 6=>"GO FISH: 6"}



Thanks for your time, and please let me know your thoughts!


Best,
Nathan Broadbent


-- 
http://bugs.ruby-lang.org/