In Ruby you can create a Hash by assigning a key to a value with => , separate these key/value pairs with commas, and enclose the whole thing with curly braces.
Ruby's arrays and hashes are indexed collections. Both store collections of objects, accessible using a key. With arrays, the key is an integer, whereas hashes support any object as a key.
Well, Ruby's hash implementation will store the items in the same bin in the form of a linked list. If the identifier (the key) matches the one stored (it checks this with equal? ), it returns the value, and if it doesn't, it simply continues traversing the list.
Simply use Hash[*array_variable.flatten]
For example:
a1 = ['apple', 1, 'banana', 2]
h1 = Hash[*a1.flatten(1)]
puts "h1: #{h1.inspect}"
a2 = [['apple', 1], ['banana', 2]]
h2 = Hash[*a2.flatten(1)]
puts "h2: #{h2.inspect}"
Using Array#flatten(1)
limits the recursion so Array
keys and values work as expected.
NOTE: For a concise and efficient solution, please see Marc-André Lafortune's answer below.
This answer was originally offered as an alternative to approaches using flatten, which were the most highly upvoted at the time of writing. I should have clarified that I didn't intend to present this example as a best practice or an efficient approach. Original answer follows.
Warning! Solutions using flatten will not preserve Array keys or values!
Building on @John Topley's popular answer, let's try:
a3 = [ ['apple', 1], ['banana', 2], [['orange','seedless'], 3] ]
h3 = Hash[*a3.flatten]
This throws an error:
ArgumentError: odd number of arguments for Hash
from (irb):10:in `[]'
from (irb):10
The constructor was expecting an Array of even length (e.g. ['k1','v1,'k2','v2']). What's worse is that a different Array which flattened to an even length would just silently give us a Hash with incorrect values.
If you want to use Array keys or values, you can use map:
h3 = Hash[a3.map {|key, value| [key, value]}]
puts "h3: #{h3.inspect}"
This preserves the Array key:
h3: {["orange", "seedless"]=>3, "apple"=>1, "banana"=>2}
The best way is to use Array#to_h
:
[ [:apple,1],[:banana,2] ].to_h #=> {apple: 1, banana: 2}
Note that to_h
also accepts a block:
[:apple, :banana].to_h { |fruit| [fruit, "I like #{fruit}s"] }
# => {apple: "I like apples", banana: "I like bananas"}
Note: to_h
accepts a block in Ruby 2.6.0+; for early rubies you can use my backports
gem and require 'backports/2.6.0/enumerable/to_h'
to_h
without a block was introduced in Ruby 2.1.0.
Before Ruby 2.1, one could use the less legible Hash[]
:
array = [ [:apple,1],[:banana,2] ]
Hash[ array ] #= > {:apple => 1, :banana => 2}
Finally, be wary of any solutions using flatten
, this could create problems with values that are arrays themselves.
Update
Ruby 2.1.0 is released today. And I comes with Array#to_h
(release notes and ruby-doc), which solves the issue of converting an Array
to a Hash
.
Ruby docs example:
[[:foo, :bar], [1, 2]].to_h # => {:foo => :bar, 1 => 2}
Edit: Saw the responses posted while I was writing, Hash[a.flatten] seems the way to go. Must have missed that bit in the documentation when I was thinking through the response. Thought the solutions that I've written can be used as alternatives if required.
The second form is simpler:
a = [[:apple, 1], [:banana, 2]]
h = a.inject({}) { |r, i| r[i.first] = i.last; r }
a = array, h = hash, r = return-value hash (the one we accumulate in), i = item in the array
The neatest way that I can think of doing the first form is something like this:
a = [:apple, 1, :banana, 2]
h = {}
a.each_slice(2) { |i| h[i.first] = i.last }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With