Ruby Hash Whitelist Filter

45,783

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"}
Share:
45,783
stellard
Author by

stellard

Updated on July 04, 2020

Comments

  • stellard
    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
    Brian Campbell about 15 years
    5 seconds behind on the typing... I knew I should have posted before testing to make sure I didn't make a typo.
  • owl
    owl about 15 years
    You can use reject! rather than setting another variable
  • Brian Campbell
    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
    stellard about 15 years
    This is good however I may not know the other hash keys. I only know the ones that I want
  • stellard
    stellard about 15 years
    but the select method returns an array. In this case I need a hash
  • Matt Connolly
    Matt Connolly about 12 years
    And if x is an ActiveRecord::Base subclass, you can do y = x.attributes.slice *%w(one two three).
  • Benjamin Oakes
    Benjamin Oakes almost 12 years
    If you're familiar with _.pick in Underscore.js, this is the same idea. (I came here looking for that!)
  • Benjamin Oakes
    Benjamin Oakes almost 12 years
    Downvote because this is a blacklist filter, but the OP wants a whitelist.
  • skalee
    skalee over 11 years
    And ActiveSupport is pretty lightweight, it's used by many non-Rails gems.
  • Martin Konecny
    Martin Konecny over 10 years
    Why the double negative? x.select { |key,_| wanted_keys.include? key }? This returns a hash for me
  • stellard
    stellard over 10 years
    It 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
    RobinGower about 10 years
    Downvote for accepting your own copy of @sris's answer! Your comment on that answer is sufficient to point out the difference.
  • stellard
    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
    RobinGower about 10 years
    Ah - I see, that comment came years later. Sorry, my vote is locked so I can't un-downvote.
  • GMA
    GMA almost 10 years
    Note you don't necessarily need to be using Rails, you can just load Active Support. Add active_support to your Gemfile and require "active_support/core_ext/hash/slice".
  • AlexChaffee
    AlexChaffee over 9 years
    find_all is an alias for select so this is basically the same as the first two answers :-)
  • guzart
    guzart over 9 years
    For 1.8.7 use reject and negate the condition
  • OneChillDude
    OneChillDude over 8 years
    Booyah! Exactly what I needed
  • Tyler Collier
    Tyler Collier about 8 years
    If you have an array of attributes like the wanted_keys in sris's answer, you could do y = x.slice(*wanted_keys)
  • stevenspiel
    stevenspiel almost 7 years
    careful with slice if you care about the hash's ordering. It will always order the output by the args passed into it.