Ruby 201: Weird Hash Syntax 6
You know how I normally rave about Ruby’s clear and natural syntax, how I prefer it over languages like C#, and generally say that Ruby is better than a baked loaf of dough cut into thin vertical strips.
But today… not so much.
Hash#select Returns… An Array?
Yes, Hash#select returns an array. I’m not sure why. I’m sure it seemed like a good idea at the time. But the other day I needed to get a “subset” of a Hash, and since Hash includes Enumerable, I thought I could do a select and get back the key-value pairs that I wanted. But, it returned a bunch of nested arrays instead.
vehicles = { :cars => 36, :boats => 8, :trains => 12, :planes => 21 }
vehicles.select { |k,v| v > 20 }
=> [[ :planes, 21], [ :cars, 36]]
Fortunately, it’s easy to turn this back into a hash. Hash overrides the class-level [ ] operator in an interesting way. If you pass it a flat list of values, it will construct a hash by assuming that the list is an alternating list of keys and values:
Hash[:apple, 'red', :banana, 'yellow']
=> { :apple=>"red", :banana=>"yellow"}
So back to the original problem: can we somehow turn [[:planes, 21], [:cars, 36]] into a flat list?
First, we “flatten” the array, which gets rid of all nested levels of the array:
a = [[:planes, 21], [:cars, 36]].flatten
=> [ :planes, 21, :cars, 36]
Close, but we still have an array, and we need a simple list of values instead.
Splat to the Rescue
We now use the Splat operator and use the Hash#[ ] class-level method to create our new hash:
h = Hash[*a]
=> { :planes=>21, :cars=>36}
Ta da!
Make It Part of Ruby
If you do this a lot, you could wrap this up in a method… but you can also choose to modify the Hash class directly:
class Hash
alias :old_select :select
def select(&block)
a = old_select(&block)
Hash[*a.flatten]
end
end
Behold, our original code now does something beautiful:
vehicles.select { |k,v| v > 20 }
=> { :planes=>21, :cars=>36}
Wow, 7 lines of code and I’ve just made Ruby bend to my will. In fact, it turns out the next version of Ruby will support this syntax directly.
You know, there are some days when I just have to say, I think Ruby is better than sliced….



Do be warned, if your hash contains any array values (or keys, for that matter) simply flattening the array from {}.select will break your data.
A safer way is to loop through the array and manually collect the paired elements:
A small improvement
Hash[*arr.inject([]){|res,(k,v)| res << k << v}]
Although it isn't a quick fix. Ruby 1.9 does fix this.
http://eigenclass.org/hiki.rb?Changes+in+Ruby+1.9#l81
I noticed this behaviour also and it looks like it is fixed by using a reject method, with a negated form of the conditions.
Your first example would then be:
vehicles.reject { |k,v| v <= 20 }
instead of:
vehicles.select { |k,v| v > 20 }
Is this presumption correct?
Our faculties are the clue to the discovery of the Categories.
But to this matter no answer is possible.