Skip to content

Instantly share code, notes, and snippets.

@adjam
Last active September 25, 2018 14:58
Show Gist options
  • Save adjam/6a34fdfc0493bb015be3b411a7b00a56 to your computer and use it in GitHub Desktop.
Save adjam/6a34fdfc0493bb015be3b411a7b00a56 to your computer and use it in GitHub Desktop.
Declaratively rename and delete key/value pairs in a ruby hash
# refinement for Hash class that allows declarative remapping functions
#
# #remap(mapping)
# and
# #remap!(mapping)
# methods.
# The `mapping` hash maps key names in the original hash to their
# names in the resulting hash, e.g.
# `{ foo: 'bar' }.remap({foo: :fu}) => `{ fu: 'bar' }`
# There are two special values that can be used in a mapping:
# `:__delete__` means that the key will be deleted, and
# if the value responds to `#call`, it will be invoked with two arguments:
# the key and value from the original hash, and it is expected to return
# an array of length 2, where the first element is the new key, and i
# the second # element is the new value.
#
# Example:
#
# convert = { foo: 'bar'
# bar: ->(k, v) { [k.upcase, "#{k}_{v}" }
# }
#
# { foo: :baz,
# bar: 'hey' }.remap(convert)
#
# =>
# { baz: 'bar', BAR: "bar_hey" }
#
# The #remap and #remap! functions are identical except the latter
# modifies the original hash in-place.
#
# To use this within a class or module, put
# `using HashUtil` below the definition of your class or module.
module HashUtil
def self.rename_hash(h, names)
names.each do |k, v|
next unless h.key?(k)
if v == :__delete__
h.delete(k)
else
if v.respond_to?(:call)
r = v.call(k, h.delete(k))
h[r[0]] = r[1]
else
h[v] = h.delete(k)
end
end
end
h
end
refine Hash do
def remap(names)
copy = Marshal.load(Marshal.dump(self))
HashUtil.rename_hash(copy, names)
end
def remap!(names)
HashUtil.rename_hash(self, names)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment