Array to Hash Ruby
Solution 1
a = ["item 1", "item 2", "item 3", "item 4"]
h = Hash[*a] # => { "item 1" => "item 2", "item 3" => "item 4" }
That's it. The *
is called the splat operator.
One caveat per @Mike Lewis (in the comments): "Be very careful with this. Ruby expands splats on the stack. If you do this with a large dataset, expect to blow out your stack."
So, for most general use cases this method is great, but use a different method if you want to do the conversion on lots of data. For example, @Łukasz Niemier (also in the comments) offers this method for large data sets:
h = Hash[a.each_slice(2).to_a]
Solution 2
Ruby 2.1.0 introduced a to_h
method on Array that does what you require if your original array consists of arrays of key-value pairs: http://www.ruby-doc.org/core-2.1.0/Array.html#method-i-to_h.
[[:foo, :bar], [1, 2]].to_h
# => {:foo => :bar, 1 => 2}
Solution 3
Just use Hash.[]
with the values in the array. For example:
arr = [1,2,3,4]
Hash[*arr] #=> gives {1 => 2, 3 => 4}
Solution 4
Or if you have an array of [key, value]
arrays, you can do:
[[1, 2], [3, 4]].inject({}) do |r, s|
r.merge!({s[0] => s[1]})
end # => { 1 => 2, 3 => 4 }
Solution 5
This is what I was looking for when googling this:
[{a: 1}, {b: 2}].reduce({}) { |h, v| h.merge v }
=> {:a=>1, :b=>2}
Related videos on Youtube
djhworld
Updated on June 02, 2021Comments
-
djhworld about 3 years
Convert this Array:
a = ["item 1", "item 2", "item 3", "item 4"]
...to a Hash:
{ "item 1" => "item 2", "item 3" => "item 4" }
i.e. elements at even indexes are keys and odd ones are values.
-
tester over 11 yearswhat does the
*
do in this example? -
Ben Lee over 11 years@tester, the
*
is called the splat operator. It takes an array and converts it a literal list of items. So*[1,2,3,4]
=>1, 2, 3, 4
. In this example, the above is equivalent to doingHash["item 1", "item 2", "item 3", "item 4"]
. AndHash
has a[]
method that accepts a list of arguments (making even indexes keys and odd indexes values), butHash[]
does not accept an array, so we splat the array using*
. -
Ben Lee over 11 years@tester Another common use case for splat is to use in reverse, to define methods with unknown number of parameters. E.g
def whatever *args
. You can pass in a list likewhatever(7, 1, 3, 4)
. And as above,*args
represents a list andargs
represents an array. Since you are passing in a list, you are actually passing in the*args
. Thusargs
is an array, and what you end up with isargs == [7, 1, 3, 4]
. So you can pass in whatever parameters you want into the method and then just useargs
to access them. -
Yossi over 11 yearsYou answer is not related to the question and in your case it's still much easier to use the same
Hash[*arr]
-
Erik Escobedo over 11 yearsNope. It would return
{ [1, 2] => [3, 4] }
. And since the question's title says "Array to Hash" and the built-in "Hash to Array" method does:{ 1 => 2, 3 => 4}.to_a # => [[1, 2], [3, 4]]
, I thought more than one could end here trying to get the inverse of the built-in "Hash to Array" method. Actually, that's how I ended here anyway. -
Yossi over 11 yearsSorry, I added a spare asterisk.
Hash[arr]
will do the job for you. -
Mike Lewis over 11 yearsBe very careful with this. Ruby expands splats on the stack. If you do this with a large dataset, expect to blow out your stack.
-
guest over 11 yearsIMHO better solution: Hash[*array.flatten(1)]
-
Ben Lee about 11 years@MikeLewis, thanks for the caveat. I've added that to my answer (with attribution to you).
-
Josh about 11 yearsAnother caveat, this method raises an exception if you pass in an array with an odd number of elements.
ArgumentError: odd number of arguments for Hash
-
Boris Stitnicky about 11 yearsYossi: Sorry for raising the dead, but there is one bigger problem with his answer, and that is the use of
#inject
method. With#merge!
,#each_with_object
should have been used. If#inject
is insisted upon,#merge
rather than#merge!
should have been used. -
Alan Coromano almost 11 yearswhat does [*arr] mean?
-
Chuck almost 11 years@Marius:
*arr
convertsarr
into an argument list, so this is calling the[]
method of Hash with the contents of arr as arguments. -
Hauleth almost 11 yearsOn big data tables you can use
Hash[a.each_slice(2).to_a]
. -
Ben Lee almost 11 years@ŁukaszNiemier, thanks for that. I've edited that into my answer (with attribution to you)
-
Kevin over 10 yearsWhat does "blow out your stack" mean?
-
Ben Lee over 10 years@Kevin, the stack uses a small area of memory that the program allocates and reserves for certain specific operations. Most commonly, it is used to keep a stack of the methods that have been called so far. This is the origin of the term stack trace, and this is also why an infinitely recursive method can cause a stack overflow. The method in this answer also uses the stack, but since the stack is only a small area of memory, if you try this method with a large array, it will fill up the stack and cause an error (an error along the same lines as a stack overflow).
-
Dennis about 10 yearsBeautiful! Much better than some of the other solutions here.
-
Hauleth almost 10 yearsNow it is possible to do
a.each_slice(2).to_h
(at least in YARV). -
AfDev over 9 yearsfor pre 2.1.0 ruby versions, you can use the Hash::[] method to get the similar results, as long as you have pairs of a nested array. so a =[[:foo, :1], [bar, 2]] --- Hash[a] => {:foo=>1, :bar=>2}
-
Jochem Schulenklopper over 9 years@AfDev, indeed, thanks. You're correct (when ignoring the minor typos:
bar
needs to be a symbol, and the symbol:2
should be an integer. So, your expression corrected isa = [[:foo, 1], [:bar, 2]]
). -
b-studios almost 9 yearsIf you don't fully hate
Hash[ ... ]
but want to use it as a chained method (like you can do withto_h
) you can combine Boris suggestions and write:arr.each_slice( 2 ).map { |e| e }.tap { |a| break Hash[a] }
-
Pikachu almost 8 yearsSimple code sample to see the error when trying to send a huge number of arguments using splat: ruby-forum.com/topic/1210854
-
Admin over 7 yearsYou don't want to use
merge
, it constructs and discards a new hash per loop iteration and is very slow. If you've got a array of hashes try[{a:1},{b:2}].reduce({}, :merge!)
instead - it merges everything into the same (new) hash. -
Thanasis Petsas over 7 yearsThanks, this is what I wanted too! :)
-
Daniel Werner about 7 yearsTo make the semantics of the code above clearer: This will create a "lazy hash" h, which is initially empty, and will pull out elements from the original array a when needed. Only then will they actually be stored in h!
-
Matthew over 5 yearsI don't think this answer is correct anymore. The linked documentation at the start of the answer says that
to_h
operates on an array of arrays. Running["item 1", "item 2", "item 3", "item 4"].to_h
results inTypeError Exception: wrong element type String at 0 (expected array)
.[["item 1", "item 2"], ["item 3", "item 4"]].to_h
works though. I think @hauleth is right, the correct answer in my testing with Ruby 2.5.3 isa.each_slice(2).to_h
-
Ben Lee over 5 yearsYup, doesn't seem to work anymore. Removed that answer. Left my original hash splat answer since that never stopped working, and was my original answer anyway.
-
Ben Lee over 5 yearsAlso the
each_slice(2).to_h
method i already had listed in my answer. -
Ben Lee about 5 yearsYou can also do
.reduce(&:merge!)
-
Ben Lee about 5 years
[{a: 1}, {b: 2}].reduce(&:merge!)
evaluates to{:a=>1, :b=>2}
-
Ben Lee about 5 yearsThis works because inject/reduce has a feature where you can omit the argument, in which case it uses the first argument of the array to operate on as the input argument, and the rest of the array as the array. Combine that with symbol-to-proc and you get this concise construction. In other words
[{a: 1}, {b: 2}].reduce(&:merge!)
is the same as[{a: 1}, {b: 2}].reduce { |m, x| m.merge(x) }
which is the same as[{b: 2}].reduce({a: 1}) { |m, x| m.merge(x) }
. -
Ben Lee about 5 yearstwo caveats: (1) this mutates the original first element of the array in place. if you don't want to do that, you either have to explicitly pass in
{}
, or use non-mutatingmerge
which has the same performance issue as described above; (2) the input array needs to have at least 1 element, otherwise you will getnil
back instead of an empty hash -
CTS_AE over 4 yearsNow days you can do this short and sweet:
[[1, 2], [3, 4]].to_h