Array to Hash Ruby

210,492

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}

Share:
210,492

Related videos on Youtube

djhworld
Author by

djhworld

Updated on June 02, 2021

Comments

  • djhworld
    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
    tester over 11 years
    what does the * do in this example?
  • Ben Lee
    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 doing Hash["item 1", "item 2", "item 3", "item 4"]. And Hash has a [] method that accepts a list of arguments (making even indexes keys and odd indexes values), but Hash[] does not accept an array, so we splat the array using *.
  • Ben Lee
    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 like whatever(7, 1, 3, 4). And as above, *args represents a list and args represents an array. Since you are passing in a list, you are actually passing in the *args. Thus args is an array, and what you end up with is args == [7, 1, 3, 4]. So you can pass in whatever parameters you want into the method and then just use args to access them.
  • Yossi
    Yossi over 11 years
    You answer is not related to the question and in your case it's still much easier to use the same Hash[*arr]
  • Erik Escobedo
    Erik Escobedo over 11 years
    Nope. 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
    Yossi over 11 years
    Sorry, I added a spare asterisk. Hash[arr] will do the job for you.
  • Mike Lewis
    Mike Lewis over 11 years
    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.
  • guest
    guest over 11 years
    IMHO better solution: Hash[*array.flatten(1)]
  • Ben Lee
    Ben Lee about 11 years
    @MikeLewis, thanks for the caveat. I've added that to my answer (with attribution to you).
  • Josh
    Josh about 11 years
    Another 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
    Boris Stitnicky about 11 years
    Yossi: 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
    Alan Coromano almost 11 years
    what does [*arr] mean?
  • Chuck
    Chuck almost 11 years
    @Marius: *arr converts arr into an argument list, so this is calling the [] method of Hash with the contents of arr as arguments.
  • Hauleth
    Hauleth almost 11 years
    On big data tables you can use Hash[a.each_slice(2).to_a].
  • Ben Lee
    Ben Lee almost 11 years
    @ŁukaszNiemier, thanks for that. I've edited that into my answer (with attribution to you)
  • Kevin
    Kevin over 10 years
    What does "blow out your stack" mean?
  • Ben Lee
    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
    Dennis about 10 years
    Beautiful! Much better than some of the other solutions here.
  • Hauleth
    Hauleth almost 10 years
    Now it is possible to do a.each_slice(2).to_h (at least in YARV).
  • AfDev
    AfDev over 9 years
    for 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
    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 is a = [[:foo, 1], [:bar, 2]]).
  • b-studios
    b-studios almost 9 years
    If you don't fully hate Hash[ ... ] but want to use it as a chained method (like you can do with to_h) you can combine Boris suggestions and write: arr.each_slice( 2 ).map { |e| e }.tap { |a| break Hash[a] }
  • Pikachu
    Pikachu almost 8 years
    Simple code sample to see the error when trying to send a huge number of arguments using splat: ruby-forum.com/topic/1210854
  • Admin
    Admin over 7 years
    You 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
    Thanasis Petsas over 7 years
    Thanks, this is what I wanted too! :)
  • Daniel Werner
    Daniel Werner about 7 years
    To 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
    Matthew over 5 years
    I 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 in TypeError 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 is a.each_slice(2).to_h
  • Ben Lee
    Ben Lee over 5 years
    Yup, 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
    Ben Lee over 5 years
    Also the each_slice(2).to_h method i already had listed in my answer.
  • Ben Lee
    Ben Lee about 5 years
    You can also do .reduce(&:merge!)
  • Ben Lee
    Ben Lee about 5 years
    [{a: 1}, {b: 2}].reduce(&:merge!) evaluates to {:a=>1, :b=>2}
  • Ben Lee
    Ben Lee about 5 years
    This 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
    Ben Lee about 5 years
    two 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-mutating merge which has the same performance issue as described above; (2) the input array needs to have at least 1 element, otherwise you will get nil back instead of an empty hash
  • CTS_AE
    CTS_AE over 4 years
    Now days you can do this short and sweet: [[1, 2], [3, 4]].to_h