tags:

views:

50

answers:

1

Lets say I have a hash which may contain hashes.

params: { :action => "index", :controller => "home", 
          :secret => "I love Jeff Atwood",
          :user => {name => "Steve", secret => "I steal Joel's pants"}}

Is there an elegant way to traverse the hash and remove all the "secret" keys I come across including the subhashes. (The hashes are not restricted so no way to know what the hashes may contain in advance.)

I know I could do

params.delete(:secret)

but that wouldn't get the secret from the 'user' hash.

+5  A: 

I don't think there is a built in method for this so a simple recursive method along the following lines is one solution:

def recursive_delete(hash, to_remove)
  hash.delete(to_remove)
  hash.each_value do |value|
    recursive_delete(value, to_remove) if value.is_a? Hash
  end
end

With your example data:

h = { :action => "index", :controller => "home", :secret => "I love Jeff Atwood",
          :user => {:name => "Steve", :secret => "I steal Joel's pants"}}

recursive_delete(h, :secret)

puts h.inspect

Gives:

{:controller=>"home", :user=>{:name=>"Steve"}, :action=>"index"}

Note that this solution works in place i.e. it is modifying the original Hash, not returning a new Hash with the requested key excluded.

mikej
You should probably call your function `recursive_delete!` to follow Ruby naming conventions.
Jeriko
@Jeriko Actually, `delete` and `delete_if` *don't* end with a bang (!) in Ruby, probably because there's no such thing as a version of those methods that's *not* destructive. (The bang vs. non-bang is for methods that vary.) String does have `delete` vs `delete!` though, so even that isn't perfectly clear.
Telemachus
Then I'd argue that `delete` and `delete_if` should also be renamed :P Sure, it's not a hard and fast rule- it's a convention that offers value if you follow it.. and sometimes confusion if you assume everyone else follows it when they don't! But hey, it's your function :D
Jeriko
@Jeriko First, it's not my function. I'm another guy. Second, you're misunderstanding *what* the convention is, in my opinion. The convention is *not* "Put a bang if the method is destructive." The convention is more like "Put a bang (on the in-place version) *if* there are *two methods*, one of which alters the object in place and the other of which does not." This situation doesn't match that rule. Nor do the built-ins `delete`, `delete_if` and `delete_at` for hashes and arrays.
Telemachus
I say "it's your function" in a generic sense - coding is subjective. People are bound to form conflicting opinions unless a documented standard exists. I believe any method that modifies (as opposed to returns) should, as a courtesy, warn developers with an `!`. I see no reason to omit the `!` just because a non-destructive version doesnt exist *at this moment in time* - what if you write one later? You would have to rename functions and potentially break legacy code. Naming conventions exist to convey intent and process, so that we can be confident of behaviour *sans* stepping through code.
Jeriko