Ruby Hash Whitelist Filter
Solution 1
Maybe this it what you want.
wanted_keys = %w[one two]
x = { "one" => "one", "two" => "two", "three" => "three"}
x.select { |key,_| wanted_keys.include? key }
The Enumerable mixin which is included in e.g. Array and Hash provides a lot of useful methods like select/reject/each/etc.. I suggest that you take a look at the documentation for it with ri Enumerable.
Solution 2
Rails' ActiveSupport library also gives you slice and except for dealing with the hash on a key level:
y = x.slice("one", "two") # => { "one" => "one", "two" => "two" }
y = x.except("three") # => { "one" => "one", "two" => "two" }
x.slice!("one", "two") # x is now { "one" => "one", "two" => "two" }
These are quite nice, and I use them all the time.
Solution 3
You can just use the built in Hash function reject.
x = { "one" => "one", "two" => "two", "three" => "three"}
y = x.reject {|key,value| key == "three" }
y == { "one" => "one", "two" => "two"}
You can put whatever logic you want into the reject, and if the block returns true it will skip that key,value in the new hash.
Solution 4
Improving a bit @scottd answer, if you are using rails and have a list of what you need, you can expand the list as parameters from slice. For example
hash = { "one" => "one", "two" => "two", "three" => "three"}
keys_whitelist = %W(one two)
hash.slice(*keys_whitelist)
And without rails, for any ruby version, you can do the following:
hash = { "one" => "one", "two" => "two", "three" => "three"}
keys_whitelist = %W(one two)
Hash[hash.find_all{|k,v| keys_whitelist.include?(k)}]
Solution 5
y = x.reject {|k,v| k == "three"}
stellard
Updated on July 04, 2020Comments
-
stellard almost 4 years
I am trying to figure out how I can filter out key and value pairs from one filter into another
For example I want to take this hash
x = { "one" => "one", "two" => "two", "three" => "three"} y = x.some_function y == { "one" => "one", "two" => "two"}
Thanks for your help
EDIT: should probably mention that in this example, I want it to behave as a whitelist filter. That is, I know what I want, not what I don't want.
-
Brian Campbell about 15 years5 seconds behind on the typing... I knew I should have posted before testing to make sure I didn't make a typo.
-
owl about 15 yearsYou can use
reject!
rather than setting another variable -
Brian Campbell about 15 years@Radar Destructive modifiers can cause problems, if for instance the hash is being passed in as an argument to a method and the caller does not expect the hash to be modified by that method. Best to be in the habit of doing non-destructive updates, and only use destructive operators when necessary.
-
stellard about 15 yearsThis is good however I may not know the other hash keys. I only know the ones that I want
-
stellard about 15 yearsbut the select method returns an array. In this case I need a hash
-
Matt Connolly about 12 yearsAnd if x is an ActiveRecord::Base subclass, you can do
y = x.attributes.slice *%w(one two three)
. -
Benjamin Oakes almost 12 yearsIf you're familiar with
_.pick
in Underscore.js, this is the same idea. (I came here looking for that!) -
Benjamin Oakes almost 12 yearsDownvote because this is a blacklist filter, but the OP wants a whitelist.
-
skalee over 11 yearsAnd
ActiveSupport
is pretty lightweight, it's used by many non-Rails gems. -
Martin Konecny over 10 yearsWhy the double negative?
x.select { |key,_| wanted_keys.include? key }
? This returns a hash for me -
stellard over 10 yearsIt depends on the ruby version you are using. This works in 1.9+ but not in 1.8.7. I'll edit the answer
-
RobinGower about 10 yearsDownvote for accepting your own copy of @sris's answer! Your comment on that answer is sufficient to point out the difference.
-
stellard about 10 years@RobinGower I selected this answer 5 years ago and at the time, this was the right answer. When I made the comment 1.8.7 was still in wide use and this still was the correct answer. I can change it now but the down vote is not warranted.
-
RobinGower about 10 yearsAh - I see, that comment came years later. Sorry, my vote is locked so I can't un-downvote.
-
GMA almost 10 yearsNote you don't necessarily need to be using Rails, you can just load Active Support. Add
active_support
to your Gemfile andrequire "active_support/core_ext/hash/slice"
. -
AlexChaffee over 9 years
find_all
is an alias forselect
so this is basically the same as the first two answers :-) -
guzart over 9 yearsFor 1.8.7 use
reject
and negate the condition -
OneChillDude over 8 yearsBooyah! Exactly what I needed
-
Tyler Collier about 8 yearsIf you have an array of attributes like the
wanted_keys
in sris's answer, you could doy = x.slice(*wanted_keys)
-
stevenspiel almost 7 yearscareful with
slice
if you care about the hash's ordering. It will always order the output by the args passed into it.