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