Dec 4, 2008

Ruby Hash#only, Hash#except

I was wondering the other day if the built-in Ruby Hash class had a way of applying a blacklist or a whitelist to filter out certain keys. I couldn't find anything, so I rolled up a little thing of my own, in case you're interested:
class Hash
def except(*blacklist)
{}.tap do |h|
(keys - blacklist).each { |k| h[k] = self[k] }
end
end

def only(*whitelist)
{}.tap do |h|
(keys & whitelist).each { |k| h[k] = self[k] }
end
end
end
Now you can do things like this:
h = {:a => 10, :b => 34, :c => "hello"}
h.only :a, :b #=> {:a => 10, :b => 34}
h.except :a, :b #=> {:c => "hello"}
Note that the code only works with Ruby 1.9 or higher, or with the andand gem installed because it depends on Object#tap.

If anybody has suggestions/improvements, or knows that this has been done already and can point me to it, feel free to comment.

5 comments:

Edward said...

Hey Rob,

Take a look at Hash#reject (which is like Hash#.dup.delete_if):

class Hash
def except(*blacklist)
self.reject {|key, value| blacklist.include?(key) }
end

def only(*whitelist)
self.reject {|key, value| !whitelist.include?(key) }
end
end

h = {:a => 10, :b => 34, :c => "hello"}

h.only :a, :b # => {:a=>10, :b=>34}
h.except :a, :b # => {:c=>"hello"}

(blogger doesn’t allow pre or code tags, so take a look at http://gist.github.com/32225)

Check out the Ruby Pickaxe’s section on the Hash class.

Rob Britton said...

Good call! Reject would probably be more efficient too.

I usually work from the RDoc, it's what comes up when I google for "ruby hash".

Edward said...

The Pickaxe pdf is totally worth picking up through illicit means or not; I actually didn’t know that #reject existed till I read your post and thought “I bet Hash has this already, and the Pickaxe will list it with examples”.

Gregory said...

If you happen to be using Rails, you can use Hash#slice.

http://apidock.com/rails/Hash/slice

Anonymous said...

you can also use except() if using rails:

http://api.rubyonrails.org/classes/Hash.html#method-i-except