Array to Hash Ruby

Okay so here's the deal, I've been googling for ages to find a solution to this and while there are many out there, they don't seem to do the job I'm looking for.

Basically I have an array structured like this

["item 1", "item 2", "item 3", "item 4"] 

I want to convert this to a Hash so it looks like this

{ "item 1" => "item 2", "item 3" => "item 4" }

i.e. the items that are on the 'even' indexes are the keys and the items on the 'odd' indexes are the values.

Any ideas how to do this cleanly? I suppose a brute force method would be to just pull out all the even indexes into a separate array and then loop around them to add the values.

Answers


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]

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}

Just use Hash.[] with the values in the array. For example:

arr = [1,2,3,4]
Hash[*arr] #=> gives {1 => 2, 3 => 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 }

This is what I was looking for when googling this:

[{a: 1}, {b: 2}].reduce({}) { |h, v| h.merge v } => {:a=>1, :b=>2}


Enumerator includes Enumerable. Since 2.1, Enumerable also has a method #to_h. That's why, we can write :-

a = ["item 1", "item 2", "item 3", "item 4"]
a.each_slice(2).to_h
# => {"item 1"=>"item 2", "item 3"=>"item 4"}

Because #each_slice without block gives us Enumerator, and as per the above explanation, we can call the #to_h method on the Enumerator object.


You could try like this, for single array

irb(main):019:0> a = ["item 1", "item 2", "item 3", "item 4"]
  => ["item 1", "item 2", "item 3", "item 4"]
irb(main):020:0> Hash[*a]
  => {"item 1"=>"item 2", "item 3"=>"item 4"}

for array of array

irb(main):022:0> a = [[1, 2], [3, 4]]
  => [[1, 2], [3, 4]]
irb(main):023:0> Hash[*a.flatten]
  => {1=>2, 3=>4}

a = ["item 1", "item 2", "item 3", "item 4"]
Hash[ a.each_slice( 2 ).map { |e| e } ]

or, if you hate Hash[ ... ]:

a.each_slice( 2 ).each_with_object Hash.new do |(k, v), h| h[k] = v end

or, if you are a lazy fan of broken functional programming:

h = a.lazy.each_slice( 2 ).tap { |a|
  break Hash.new { |h, k| h[k] = a.find { |e, _| e == k }[1] }
}
#=> {}
h["item 1"] #=> "item 2"
h["item 3"] #=> "item 4"

Need Your Help

Algorithm to determine if array contains n...n+m?

arrays algorithm big-o puzzle

I saw this question on Reddit, and there were no positive solutions presented, and I thought it would be a perfect question to ask here. This was in a thread about interview questions:

Set maximum value (upper bound) in pandas DataFrame

python pandas dataframe max

I'm trying to set a maximum value of a pandas DataFrame column. For example: