Skip to content

Instantly share code, notes, and snippets.

@davide125
Last active August 7, 2020 20:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davide125/6d95da4b4feb9f4a12f2311c0802a1a1 to your computer and use it in GitHub Desktop.
Save davide125/6d95da4b4feb9f4a12f2311c0802a1a1 to your computer and use it in GitHub Desktop.
# filter_hash() takes two Hash objects and applies the second as a filter
# on the first one.
#
# Usage:
# filter_hash(hash, filter)
#
# Arguments:
# hash: Required Hash. The Hash to be filtered
# filter: Required Hash. The filter to apply against hash
#
# Sample usage:
# hash = {
# 'foo' => 1,
# 'bar' => 2,
# 'baz' => {
# 'cake' => 'asdf',
# 'pie' => 42,
# },
# }
#
# filter = ['foo', 'baz/cake']
#
# filter_hash(hash, filter) = {
# 'foo' => 1,
# 'baz' => {
# 'cake' => 'asdf',
# },
# }
def self.filter_hash(hash, filter)
self._filter_hash(hash, self._expand_filter(filter))
end
# helper method to convert AttributeAllowlist-style filters to something
# usable by _filter_hash
def self._expand_filter(array_filter)
hash_filter = {}
array_filter.each do |f|
keys = f.split('/')
h = nil
keys.reverse.each do |k|
h = { k => h }
end
hash_filter.merge!(h)
end
hash_filter
end
# private method to implement filter_hash using recursion
def self._filter_hash(hash, filter, depth = 0)
unless filter.is_a?(Hash)
fail 'fb_helpers: the filter argument to filter_hash needs to be a ' +
"Hash (actual: #{filter.class})"
end
filtered_hash = {}
# We loop over the filter and pull out only allowed items from the hash
# provided by the user. Since users might pass in something huge like
# the entire saved node object, don't make the performance of this code
# be defined by them.
filter.each do |k, v|
if hash.include?(k)
if v.nil?
filtered_hash[k] = hash[k]
elsif v.is_a?(Hash)
# we need to go deeper
ret = self._filter_hash(hash[k], v, depth + 1)
# if the filter returned nil, it means it had no matches, so
# don't add it to the filtered_hash to avoid creating spurious
# entries
unless ret.nil?
filtered_hash[k] = ret
end
else
fail "fb_helpers: invalid filter passed to filter_hash: #{filter}"
end
end
end
# if we're recursing and get an empty hash here, it means we had no
# matches; change it to nil so we can detect it appropriately in the
# loop above
if depth > 0 && filtered_hash == {}
filtered_hash = nil
end
filtered_hash
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment