Converting Ruby array into a hash

后端 未结 6 1889
甜味超标
甜味超标 2021-01-29 15:54

I am attempting to write a method named my_transform that takes an array as follows:

items = [\"Aqua\", \"Blue\", \"Green\", \"Red\", \"Yellow\"]


        
相关标签:
6条回答
  • 2021-01-29 16:24

    Most Rubies

    This works at least as far back as Ruby 1.9.3.

    # Verbose, but flexible!
    def hasherize *array
      hash = {}
      array.flatten!
      array.each_with_index { |key, value| hash[key] = value }
      hash
    end
    
    # Pass a single array as an argument.
    hasherize %w(Aqua Blue Green Red Yellow)
    #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
    
    # Pass multiple arguments to the method.
    hasherize :foo, :bar, :baz
    #=> {:foo=>0, :bar=>1, :baz=>2}
    

    Ruby >= 2.1.0

    If you're running a recent Ruby, you can simplify the above to:

    def hasherize *array
      array.flatten.each_with_index.to_h
    end
    

    The results will be the same as above, but the Array#to_h method simplifies the code a lot. However, you still need to flatten the array to avoid results like:

    #=> {["Aqua", "Blue", "Green", "Red", "Yellow"]=>0}
    
    0 讨论(0)
  • 2021-01-29 16:28

    I would use Array#to_h:

    items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
    items.each_with_index.to_h
    #=> { "Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4 }
    

    Note that to_h was introduced in Ruby 2.1

    Using to_h your my_transform method could look like this:

    def my_transform(items)
      items.each_with_index.to_h
    end
    
    0 讨论(0)
  • def my_transform(arr)
      arr.inject({}) {|m,e| m[e] = arr.index(e); m }
    end
    
    0 讨论(0)
  • items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
    def my_transform(items)     
       Hash[items.each_with_index.map { |value, index| [value, index] }]
    end
    
    0 讨论(0)
  • 2021-01-29 16:31

    You can do this in various ways.

    Create an array and convert it to a hash

    Until fairly recently, you would use the public class method Hash::[] to convert an array to a hash. It works like this:

    h = Hash[ [[:a, 1], [:b, 2]] ]
      #=> {:a=>1, :b=>2}
    

    or

    h = Hash[:a, 1, :b, 2]
      #=> {:a=>1, :b=>2}
    

    In Ruby v2.1.0 the methods Array#to_h and Enumerable#to_h were introduced. The first works like this:

    h = [[:a, 1], [:b, 2]].to_h
      #=> {:a=>1, :b=>2}
    

    Therefore, to use Hash or to_h you must first create the array:

    arr1 = [["Aqua", 0], ["Blue", 1], ["Green", 2], ["Red", 3], ["Yellow", 4]]
    

    or

    arr2 = ["Aqua", 0, "Blue", 1, "Green", 2, "Red", 3, "Yellow", 4]
    

    In the second case we'd use it like this:

    Hash[*arr2]
      #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
    

    Let's first create arr1. You are right that you need to use Enumerable#each_with_index. You then need to use Enumerable#to_a to convert each element of items to an array [<color>, index].

    items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
    
    arr = items.each_with_index.to_a
      #=> [["Aqua", 0], ["Blue", 1], ["Green", 2], ["Red", 3], ["Yellow", 4]]
    

    Let's look at this more closely:

    enum = items.each_with_index 
      #=> #<Enumerator: ["Aqua", "Blue", "Green", "Red", "Yellow"]:each_with_index>
    

    enum, an enumerator, is an instance of the class Enumerator. The Enumerator class is one of many classes that includes the Enumerable module, of which to_a is an instance method. Not only does:

    arr = enum.to_a
      #=> [["Aqua", 0], ["Blue", 1], ["Green", 2], ["Red", 3], ["Yellow", 4]]
    

    convert the enumerator to the desired array, but it is a convenient way to view the elements of any enumerator (which are generally passed to either a block or to another enumerator).

    So we can now create the hash:

    h = Hash[arr]   
      #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
    

    or

    h = Hash[*arr.flatten]
      #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
    

    or

    h = arr.to_h
      #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
    

    Suppose now that we had:

    items = ["Aqua", "Blue", "Green", "Aqua", "Aqua"]
    

    We then obtain:

    items.each_with_index.to_a.to_h
      #=> {"Aqua"=>4, "Blue"=>1, "Green"=>2}
    

    In building the hash, Ruby first creates the key-value pair "Aqua"=>0, which she later overwrites with "Aqua"=>3 and then with "Aqua"=>4. This is a consequence of the fact that hashes have unique keys.

    Build the hash from scratch

    Now suppose we start with an empty hash:

    h = {}
    

    (same as h = Hash.new) and add key-value pairs:

    items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
    items.each_index { |i| h[items[i]] = i }
      #=> ["Aqua", "Blue", "Green", "Red", "Yellow"] 
    h #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4} 
    

    We could alternatively write:

    items.size.times { |i| h[items[i]] = i }
      #=> 5
    h #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4} 
    

    or

    (0...items.size).each { |i| h[items[i]] = i }
      #=> 0...5 
    h #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4} 
    

    The Ruby way is skip the step h = {} and to use each_with_index, as before, together with Enumerator#with_object:

    items.each_with_index.with_object({}) { |(s,i),h| h[s] = i }
      #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4} 
    

    The "object" in with_object is a hash, with_object's argument being its initial value, here an empty hash. This object is represented by the block variable h and is returned after all elements of items have been enumerated (so we don't need a subsequent line h to return the hash).

    Lets look at the steps that are performed here. First, we have

    enum0 = items.each_with_index
      #=> #<Enumerator: ["Aqua", "Blue", "Green", "Red", "Yellow"]:each_with_index> 
    

    which I discussed earlier. Then Ruby computes

    enum1 = enum0.with_object({})
      #=> #<Enumerator: #<Enumerator: ["Aqua", "Blue", "Green", "Red", "Yellow"]
            :each_with_index>:with_object({})> 
    

    Examine the return value carefully. As you see, enum1, like enum0, is an enumerator. You might think of it as a "compound enumerator". To see the values of enum1 that will be passed to the block, you can convert it to an array:

    enum1.to_a
      #=> [[["Aqua", 0], {}], [["Blue", 1], {}], [["Green", 2], {}],
      #    [["Red", 3], {}], [["Yellow", 4], {}]] 
    

    As you see, enum1 has five elements, each an array containing an array and a hash. The elements of enum1 are passed to the block by Enumerator#each, (which calls Array#each):

    enum1.each { |(s,i),h| h[s] = i }
      #=> {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
    

    We can use Enumerator#next to pass each element of enum1 to the block, and set the block variables to its value. The first is:

    (s,i),h = enum1.next
      #=> [["Aqua", 0], {}] 
    s #=> "Aqua" 
    i #=> 0 
    h #=> {} 
    

    Notice how [["Aqua", 0], {}] is decomposed into its three constituent elements and each block variable is set equal to one of the elements. This is called array decomposition.

    We can now perform the block calculation:

    h[s] = i
     #=> {}["Aqua"] = 0
    

    so now:

    h #=> {"Aqua"=>0}
    

    Then the second element is passed to the block:

    (s,i),h = enum1.next
      #=> [["Blue", 1], {"Aqua"=>0}] 
      s #=> "Blue" 
      i #=> 1 
      h #=> {"Aqua"=>0} 
    

    Notice how h has been updated. The block calculation is now:

    h[s] = i
     #=> {"Aqua"=>0}["Blue"] = 1
    

    and now:

    h #=> {"Aqua"=>0, "Blue"=>1}
    

    The remaining calculations are performed similarly. After all elements of enum1 have been enumerated, enum1.each returns h.

    0 讨论(0)
  • 2021-01-29 16:44

    You can also try this.

    e.g.

    items = ["Aqua", "Blue", "Green", "Red", "Yellow"]
    
    items.inject({}) do |tmphash, (k,v)|
      tmphash[k] = items.index(k)
      tmphash
    end
    
    ## OUTPUT
    
    {"Aqua"=>0, "Blue"=>1, "Green"=>2, "Red"=>3, "Yellow"=>4}
    
    0 讨论(0)
提交回复
热议问题