Last Updated: February 25, 2016
·
5.809K
· gus

Deleting nested hashes

When dealing with hashes Rails exposes a nifty method called except which duplicates the original hash and returns its copy removing the given arguments.

I wanted to expose a similar operator for excluding hashes inside hashes given the nested key. I tried to approach the problem by naïvely duplicating Rails's except method:

class Hash
  def except_nested_key(key)
    dup.except_nested_key!(key)
  end

  def except_nested_key!(key)
    each { (k, v) v.delete(key) if v.is_a? Hash }

    self
  end
end

The problem with this approach is that both dup and clone make a shallow copy of the object, meaning that even though the original object references are being copied, the objects those references refer to are not. Which means that regardless of the method called, both of them perform destructive actions on the original object.

Fortunately we can achieve a deep copy thanks to Marshal. From the Ruby documentation:

"The marshaling library converts collections of Ruby objects into a byte stream, allowing them to be stored outside the currently active script. This data may subsequently be read and the original objects reconstituted."

Making use of Marshal we can re-implement the previous method in the following form:

class Hash
  def except_nested_key(key)
    copy = Marshal.load(Marshal.dump(self))

    copy.each { |(k, v)| v.delete(key) if v.is_a? Hash }
  end
end