Selecting matching key/value pairs from a hash

I ran into a situation today where I needed to pull out all key/value pairs from a hash that matched the keys in a pre-existing array. This is what I initially came up with:

hash = { :foo => "foo", :bar => "bar", :bat => "bat" }
hash.symbolize_keys.reject { |k, v| ![:foo, :bar].include?(k) }

>>> returns { :foo => "foo", :bar => "bar" }

Kind of ugly, ain’t it? I sure don’t want to repeat those lines elsewhere. Let’s see if we can do better:

class Hash
  def select_all(*attrs)
    symbolize_keys.reject { |k, v| !attrs.include?(k) }
  end
end

{ :foo => "foo", :bar => "bar", :bat => "bat" }.select_all(:foo, :bar)

>>> returns { :foo => "foo", :bar => "bar" }

Now that’s more like it. Extending Hash lets me call a method directly on my hash. I can also use this method anywhere in my Rails application if I place the Hash code in lib/hash.rb and require 'hash' from my environment.rb file. Isn’t Ruby beautiful?

Anyone else have a better way of doing this? I’m open to suggestions.

3 thoughts on “Selecting matching key/value pairs from a hash

  1. I would call it #select_keys, and I would glob lib/ in environment.rb.

  2. Pit e-mailed me directly with this alternate solution:

    def select_all(*attrs)
      (keys & attrs).inject({}) { |h, k| h[k] = self[k] }
    end
    

    Not bad. He says that depending on the size of the hash and the number of attributes, this
    implementation could be faster.

  3. Mike sends in another example for us, one that doesn’t copy the entire hash using symbolize_keys first:

    class Hash
     def select_all(*attrs)
       reject { |k, v| !attrs.include?(k.to_sym) }
     end
    end
    

Comments are closed.