On my current project we have a hash named line items. This contains multiple hashes, and arrays of hashes, that will eventually have a :total key. You might also find yourself with a similar hash when consuming public APIs.
line_items = {
item_a: {
name: "Cookie",
total: 13
},
department_x: {
item_b: {
name: "s",
total: 7
}
},
items: [
{
name: "H",
total: 8
},
{
name: "Q",
total: 9
}
]
}
An obvious use of this would to calculate a grand total by summing all of the totals. Not finding exactly what I was looking for online, I wrote this little method to help the calculation by fetching all of the values that have a key :total.
class Hash
def find_all_values_for(key)
result = []
result << self[key]
self.values.each do |hash_value|
values = [hash_value] unless hash_value.is_a? Array
values.each do |value|
result += value.find_all_values_for(key) if value.is_a? Hash
end
end
result.compact
end
end
This solution is essentially a depth first search implemented with recursion. It will return an array of all the values the key points to.
line_items.find_all_values_for(:total)
# => [13, 7, 8, 9]
line_items.find_all_values_for(:total).sum
# => 37
line_items.find_all_values_for(:name).sum
# => "CookiesHQ"