Skip to content

Instantly share code, notes, and snippets.

@jrunning
Created November 30, 2012 23:32
Show Gist options
  • Select an option

  • Save jrunning/4179488 to your computer and use it in GitHub Desktop.

Select an option

Save jrunning/4179488 to your computer and use it in GitHub Desktop.
Hash#deep_exclude
require 'test/unit/assertions'
include Test::Unit::Assertions
class Hash
# A convenience method for removing deeply-nested keys from a Hash
#
# Usage:
#
# hash = { :a => { :m => 13 }, :b => 2 }
#
# hash.deep_exclude(:a) # ⇒ { :b => 2 }
# hash.deep_exclude(:a => :m) # ⇒ { :a => {}, :b => 2 }
# hash.deep_exclude(:b, :a => :m) # ⇒ { :a => {} }
# hash.deep_exclude(:a, :b) # ⇒ {}
#
# TODO:
#
# * Fix some edge cases (see tests)
#
def deep_exclude *excludes
result = self.dup
return result if excludes.empty?
exclude = excludes.shift
if exclude.is_a? Hash
exclude.each do |key,val|
if result.key?(key) && result[key].respond_to?(:deep_exclude)
result[key] = result[key].deep_exclude *val
end
end
else
result.delete exclude
end
result.deep_exclude *excludes
end
end
hash = { a: 1, b: 2 }
assert_equal( { b: 2 }, hash.deep_exclude(:a) )
assert_equal( {}, hash.deep_exclude(:a, :b) )
assert_equal( { b: 2 }, hash.deep_exclude(:a, :z) )
# All values are compared only to the hash's keys (no match on values)
assert_equal( { a: 1, b: 2 }, hash.deep_exclude(a: 1) )
# Falsy keys work too
hash = { nil => 1, false => 2 }
assert_equal( { nil => 1 }, hash.deep_exclude(false) )
assert_equal( { false => 2 }, hash.deep_exclude(nil) )
hash = { :a => { nil => 0 } }
#assert_equal( { :a => {} }, hash.deep_exclude(:a => nil) )
# Keys that are arrays are allowed
key = [:a, :b]
hash = { key => 1, :b => 2, :c => 3 }
assert_equal( { b: 2, c: 3}, hash.deep_exclude(key) )
# If the array contains the intended keys, expand it with splat first
assert_equal( { key => 1, c: 3}, hash.deep_exclude(*key) )
hash = { a: { m: 13 }, b: { n: 14 } }
# The entire subtree is removed from the given root
assert_equal( { a: { m: 13 } },
hash.deep_exclude(:b) )
# A nested root can be given
assert_equal( { a: { m: 13 }, b: {} },
hash.deep_exclude(:b => :n) )
# Nth-level arguments only match Nth level
assert_equal( { a: { m: 13 } },
hash.deep_exclude(:b, :m) )
# Empty hashes are left in place
assert_equal( { b: {} },
hash.deep_exclude(:a, :b => :n) )
# Only keys are matched (as above)
assert_equal( { a: { m: 13 }, b: { n: 14 } },
hash.deep_exclude(:b => { :n => 14 }) )
hash = { a: { m: { z: 1 } } }
assert_equal( { a: { m: {} } },
hash.deep_exclude(:a => { :m => :z }) )
hash = { a: 1, b: { m: 13, n: 14, o: { x: 24, y: 25 } } }
# An array can be given for levels after the first
assert_equal( { a: 1, b: { n: 14 } },
hash.deep_exclude(:b => [ :m, :o ]) )
assert_equal( { a: 1, b: { m: 13, n: 14, o: { y: 25 } } },
hash.deep_exclude(:b => [ { :o => :x } ] ) )
hash = { a: { m: 13, n: { x: 24, y: 25 } }, b: 2, c: { o: 15 } }
# Arrays can contain hashes
assert_equal( { a: { n: { x: 24 } }, b: 2 },
hash.deep_exclude(:c, { :a => [ :m, { :n => :y } ] }) )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment