Jan 26, 2009

Ruby Array#to_hash

The number of times I've had to convert an array to a hash is amazing. And the sad thing is that the standard Ruby array doesn't have any simple way to do it! This is probably because there are a lot of ways you can convert an array to a hash.

Here's the way I do it a lot:
hash = Hash[*keys.zip(values).flatten]
This works, unless your values array has nested arrays in which case you're screwed. A better alternative is:
hash = keys.zip(values).inject({}) { |h, nvp| h[nvp[0]] = nvp[1]; h }
This handles nested arrays, but it makes Ruby elegance freaks cry. But that's not my problem, so let's go like this:
class Array
def to_hash
self.inject({}) { |h, nvp| h[nvp[0]] = nvp[1]; h }
Assuming that the array is in name-value pair format, this should work fine. So you have:
[ [:a, 5], [:b, "hello"] ].to_hash   # => {:a => 5, :b => "hello" }
There are probably problems with this, if you know of any please comment!


outis said...

No need to flatten and splat the array. If Hash.[] is passed an array of [key, value] pairs, it apparently unpackages the outermost array.

>> keys=[:zero, :one, :two, :three]
>> values=(0..3).collect { |i| (0...i).to_a }
>> Hash[ keys.zip(values) ]
=> {:three=>[0, 1, 2], :zero=>[], :one=>[0], :two=>[0, 1]}

Rob Britton said...

Ahhh, nice. I think when I first tried it I did something like this:

>> Hash[ [:a, :b], [:c, :d] ]
=> {[:a, :b]=>[:c, :d]}

So I assumed you had to splat the array. Nice to know!