Sum 2 Hashes Attributes with the Same Key

Sum 2 hashes attributes with the same key


a_hash = {'a' => 30, 'b' => 14}
b_hash = {'a' => 4, 'b' => 23, 'c' => 7}

a_hash.merge(b_hash){ |k, a_value, b_value| a_value + b_value }
=> {"a"=>34, "b"=>37, "c"=>7}

b_hash.merge(a_hash){ |k, b_value, a_value| a_value + b_value }
=> {"a"=>34, "b"=>37, "c"=>7}

Group hashes by keys and sum the values


ar = [{"Vegetable"=>10}, {"Vegetable"=>5}, {"Dry Goods"=>3}, {"Dry Goods"=>2}]
p ar.inject{|memo, el| memo.merge( el ){|k, old_v, new_v| old_v + new_v}}
#=> {"Vegetable"=>15, "Dry Goods"=>5}

Hash.merge with a block runs the block when it finds a duplicate; inject without a initial memo treats the first element of the array as memo, which is fine here.

how do I merge array of hashes summing by the value?

Let's group by id and map each result to a structure where the pages are the sum of all the pages for that id

array.
group_by { |item| item[:id] }.
map do |id, items|
page_sum = items.sum { |i| i[:pages] }
Hash[:id, id, :book, items.first[:book], :pages, page_sum]
end

Get sum of key values in deep array of hashes

Regarding ruby-on-rails tag, you can use Ruby method #inject with Rails method #deep_merge:

array.inject do |hash1, hash2|
hash1.deep_merge(hash2) do |key, hash1_val, hash2_val|
hash1_val + hash2_val
end
end

=> {:john=>{:win=>30, :lose=>60}, :mike=>{:win=>15, :lose=>15}}

compute hash value of same key ruby

If you just want to compute equal keys in the hash what you are looking for is the merge method in the Hash class.

https://ruby-doc.org/core-2.2.1/Hash.html#method-i-merge

Returns a new hash containing the contents of other_hash and the
contents of hsh. If no block is specified, the value for entries with
duplicate keys will be that of other_hash. Otherwise the value for
each duplicate key is determined by calling the block with the key,
its value in hsh and its value in other_hash.

When you pass a block to the merge method it will yield both old value and new value, and the you can do your computation there.

For instance:

hash  = {"a"=>2, "b"=>3}
hash2 = {"c"=>4, "a"=>5}

result = hash.merge(hash2) { |key, old_val, new_val| old_val + new_val }

p result #=> {"a"=>7, "b"=>3, "c"=>4}

Merge or sum 2 arrays on keys in ruby

You could do it thusly:

(a + b).group_by(&:first).map { |k, v| [k, v.map(&:last).inject(:+)] }

First you put the arrays together with + since you don't care about a and b, you just care about their elements. Then the group_by partitions the combined array by the first element so that the inner arrays can easily be worked with. Then you just have to pull out the second (or last) elements of the inner arrays with v.map(&:last) and sum them with inject(:+).

For example:

>> a = [[1,10],[2,20],[3,30]]
>> b = [[1,50],[3,70]]
>> (a + b).group_by(&:first).map { |k,v| [k, v.map(&:last).inject(:+)] }
=> [[1, 60], [2, 20], [3, 100]]

Sum ruby hash values

I offer the following observations on your inject code:

  • none of the variables need be instance variables; local variables (no @) would suffice;
  • test.group_by {|i| i.type}... should be test.group_by {|i| i["type"]}...
  • @sortable_additions[key]=value should raise an exception because the hash has not been created;
  • @sorted_additions.shift removes the first element of the hash and returns that element, but there is no variable to receive it (e.g.,, h = @sorted_additions.shift);
  • @additions_per_security = Hash[@sorted_additions.map { |key, value|[key, value]}] appears to convert @sorted_additions to an array and then back to the same hash.

The following is one way to do what you you want to do.

Firstly, you will be passing date objects. To work with that we'll start by making those date objects for the dates you have in your example:

require 'date'
date1 = Date.parse("Thu, 21 Nov 2013") # => #<Date: 2013-11-21 ((2456618j,0s,0n),+0s,2299161j)>
date2 = Date.parse("Thu, 14 Nov 2013") # => #<Date: 2013-11-14 ((2456611j,0s,0n),+0s,2299161j)>
date3 = Date.parse("Thu, 20 Nov 2013") # => #<Date: 2013-11-20 ((2456617j,0s,0n),+0s,2299161j)>
date4 = Date.parse("Thu, 11 Nov 2013") # => #<Date: 2013-11-11 ((2456608j,0s,0n),+0s,2299161j)>

For testing:

test = [{"total"=>18, "type"=>"buy", "date"=>date1, "instrument_code"=>"food"},
{"total"=>92, "type"=>"buy", "date"=>date2, "instrument_code"=>"food"},
{"total"=>12, "type"=>"buy", "date"=>date3, "instrument_code"=>"drink"},
{"total"=> 1, "type"=>"buy", "date"=>date4, "instrument_code"=>"food"}]

Now we calculate what we need.

test_buy = test.select {|h| h["type"] == "buy"}

earliest = test_buy.min_by {|h| h["date"]}["date"]
# => #<Date: 2013-11-11 ((2456608j,0s,0n),+0s,2299161j)>

all_but_last = test.reject {|h| h["date"] == earliest}
# => [{"total"=>18, "type"=>"buy", "date"=>date1, "instrument_code"=>"food"},
{"total"=>92, "type"=>"buy", "date"=>date2, "instrument_code"=>"food"},
{"total"=>12, "type"=>"buy", "date"=>date3, "instrument_code"=>"drink"}]

or we could have used Enumerable#select:

all_but_last = test.select {|h| h["date"] != earliest}

Note that here and below, the values of date1, date2 and date3 will be displayed (e.g., #<Date: 2013-11-21 ((2456618j,0s,0n),+0s,2299161j)> will be displayed for date1); I've used the variable names here as placeholders to make this more readable. Also, all hashes hwith h["date"] = earliest will be rejected (should there be more than one).

grouped = all_but_last.group_by {|h| h["instrument_code"]}
# => {"food" =>[{"total"=>18, "type"=>"buy", "date"=>date1, "instrument_code"=>"food"},
{"total"=>92, "type"=>"buy", "date"=>date2, "instrument_code"=>"food"}],
"drink"=>[{"total"=>12, "type"=>"buy", "date"=>date3, "instrument_code"=>"drink"}]}

keys = grouped.keys # => ["food", "drink"]

arr = keys.map {|k| [k, grouped[k].reduce(0) {|t,h| t + h["total"]}]}
# => [["food", 110], ["drink", 12]]

Hash[arr] # => {"food"=>110, "drink"=>12}

I have used a few temporary variables, including test_buy, earliest, all_but_last, grouped, keys and arr. You can eliminate some of these by "chaining". Here I'll show you how to get rid of some of them:

test_buy = test.select {|h| h["type"] == "buy"}
earliest = test_buy.min_by {|h| h["date"]}["date"]
grouped = test_buy.reject {|h| h["date"] == earliest}.group_by \
{|h| h["instrument_code"]}
Hash[grouped.keys.map {|k| [k, grouped[k].reduce(0) \
{|t,h| t + h["total"]}]}] # => {"food"=>110, "drink"=>12}

You may think this looks complicated, but after you gain experience with Ruby, it will look very natural and read easily. The extent to which you use chaining is a style preference, however.

Merge hashes containing same key & value pair

You can compute the desired result as follows.

def doit(arr)
arr.each_with_object({}) do |g,h|
h.update(g.reject { |k,_| k==:entity_ids }=>g) do |_,o,n|
o.merge(entity_ids: o[:entity_ids] + n[:entity_ids])
end
end.values
end

doit(arr1)
#=> [{:entity_type=>"Mac", :entity_ids=>[3, 2], :cascade_id=>2, :location_id=>1},
# {:entity_type=>"Mac", :entity_ids=>[9, 10], :cascade_id=>4, :location_id=>1}]
doit(arr2)
#=> [{:entity_type=>"Sub", :entity_ids=>[7, 10], :mac_id=>5, :cascade_id=>1,
# :location_id=>1},
# {:entity_type=>"Sub", :entity_ids=>[4], :mac_id=>2, :cascade_id=>1,
# :location_id=>1},
# {:entity_type=>"Sub", :entity_ids=>[11], :mac_id=>7, :cascade_id=>2,
# :location_id=>2}]

This uses the form of Hash#update (aka merge!) that employs a block to determine the values of keys that are present in both hashes being merged. See the doc for an explanation of the block variables k, o and n.

If doit's argument is arr1, the steps are as follows.

arr = arr1
e = arr.each_with_object({})
#=> #<Enumerator: [{:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2,
# :location_id=>1},
# {:entity_type=>"Mac", :entity_ids=>[2], :cascade_id=>2,
# :location_id=>1},
# {:entity_type=>"Mac", :entity_ids=>[9], :cascade_id=>4,
# :location_id=>1},
# {:entity_type=>"Mac", :entity_ids=>[10], :cascade_id=>4,
# :location_id=>1}
# ]:each_with_object({})>

The first element of the enumerator is passed to the block and values are assigned to the block variables.

g, h = e.next
#=> [{:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2, :location_id=>1}, {}]
g #=> {:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2, :location_id=>1}
h #=> {}

Compute the (only) key for the hash to be merged with h.

a = g.reject { |k,_| k==:entity_ids }
#=> {:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}

Perform the update operation.

h.update(a=>g)
#=> {{:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}=>
# {:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2, :location_id=>1}}

This is the new value of h. As h (which was empty) did not have the key

{:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}

the block was not used to determine the value of this key in the merged hash.

Now generate the next value of the enumerator e, pass it to the block, assign values to the block variables and perform the block calculation.

g, h = e.next
#=> [{:entity_type=>"Mac", :entity_ids=>[2], :cascade_id=>2, :location_id=>1},
# {{:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}=>
# {:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2, :location_id=>1}}]
g #=> {:entity_type=>"Mac", :entity_ids=>[2], :cascade_id=>2, :location_id=>1}
h #=> {{:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}=>
# {:entity_type=>"Mac", :entity_ids=>[3, 2], :cascade_id=>2, :location_id=>1}}
a = g.reject { |k,_| k==:entity_ids }
#=> {:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}
h.update(a=>g) do |_,o,n|
puts "_=#{_}, o=#{o}, n=#{n}"
o.merge(entity_ids: o[:entity_ids] + n[:entity_ids])
end
#=> {{:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}=>
# {:entity_type=>"Mac", :entity_ids=>[3, 2], :cascade_id=>2, :location_id=>1}}

This is the new value of h. As both g and h have the key a the block is consulted to obtain the value of that key in the merged hash (new h). The values of that block variables are printed.

_={:entity_type=>"Mac", :cascade_id=>2, :location_id=>1},
o={:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2, :location_id=>1},
n={:entity_type=>"Mac", :entity_ids=>[2], :cascade_id=>2, :location_id=>1}

h[:entity_ids] is therefore replaced with

o[:entity_ids] + o[:entity_ids]
#=> [3] + [2] => [3, 2]

The calculations for the two remaining elements of e are similar, at which time

h #=> {{ :entity_type=>"Mac", :cascade_id=>2, :location_id=>1 }=>
# { :entity_type=>"Mac", :entity_ids=>[3, 2], :cascade_id=>2, :location_id=>1 },
# { :entity_type=>"Mac", :cascade_id=>4, :location_id=>1 }=>
# { :entity_type=>"Mac", :entity_ids=>[9, 10], :cascade_id=>4, :location_id=>1 }}

The final step is to return the values of this hash.

h.values
#=> <as shown above>

Note that some of the block variables are underscores (_). Though they are valid local variables, they are commonly used to indicate that they are not used in the block calculation. An alternative convention is to have the unused block variable begin with an underscore, such as _key.



Related Topics



Leave a reply



Submit