Sum Hash Values Ruby

Sum the value of array in hash

You can use inject to sum all the amounts. You can then just put the result back into a hash if you need to.

arr = [{:amount=>10, :gl_acct_id=>1, :alt_amount=>20}, {:amount=>20, :gl_acct_id=>2, :alt_amount=>30}]    
amount = arr.inject(0) {|sum, hash| sum + hash[:amount]} #=> 30
{:amount => amount} #=> {:amount => 30}

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.

Sum of particular values in a Ruby hash

A straightforward way is to say exactly what you mean:

first.map { |k, v| [ k, [ v.values_at(*second[k]).sum ] ] }.to_h

If your version of Ruby doesn't have Array#sum then use inject(:+):

first.map { |k, v| [ k, [ v.values_at(*second[k]).inject(:+) ] ] }.to_h

You could also skip the map/to_h business by using each_with_object instead:

first.each_with_object({}) { |(k, v), h| h[k] = [ v.values_at(*second[k]).inject(:+) ] }

A little time with the Array#values_at documentation might be fruitful as would the Enumerable documentation.

Rails sum values in an array of hashes

You could use each_with_object with a Hash and a default value:

array = [
{loading: 10, avg: 15, total: 25 },
{loading: 20, avg: 20, total: 40 },
{loading: 30, avg: 25, total: 55 }
]

sum = Hash.new(0)

array.each_with_object(sum) do |hash, sum|
hash.each { |key, value| sum[key] += value }
end
# => {:loading=>60, :avg=>60, :total=>120}

It will work with any number of keys, and won't complain if a key isn't present in all the hashes.


BTW, you can replace

array.map { |h| h[:loading] }.sum

with

array.sum { |h| h[:loading] }

Get the sum of all keys in ruby

The keys in your example are strings, the values are integers. If you want the sum of the integers you can do hash.values.sum:

{"dog"=>1, "cat"=>3, "fish"=>5}.values.sum
#=> 9

Sum values in array of hash if they have the same value

This seems work

array.group_by { |item| [item[:loading], item[:avg]] }.values.flat_map { |items| items.first.merge(total: items.sum { |h| h[:total] }) }
=> [{:loading=>10, :avg=>15, :total=>25}, {:loading=>20, :avg=>20, :total=>120}, {:loading=>30, :avg=>25, :total=>55}, {:loading=>10, :avg=>20, :total=>46}]

Sum of hash value inside the array In ruby

Input

data = 
[
{ :lineItemKey=>57, :sku=>"12GA-RIO",
:name=>"12 Gauge", :quantity=>4, :unitPrice=>5.76 },
{ :lineItemKey=>168, :sku=>"22LR-CAS-40-CPRN-50",
:name=>"22 Long", :quantity=>2, :unitPrice=>7.6 },
{ :lineItemKey=>57, :sku=>"12GA-RIO",
:name=>"12 Gauge", :quantity=>1, :unitPrice=>5.76 },
{ :lineItemKey=>236, :sku=>"300BLK-UNWD-CTC-220-BTHP-20",
:name=>"300 BLK", :quantity=>1, :unitPrice=>31.2 }
]

Code

p data.group_by { |x| x[:lineItemKey] }
.values
.map { |arr| arr.reduce { |h1, h2| h1.merge(h2) { |k, oldv, newv| k.eql?(:quantity) ? oldv += newv : oldv } } }

Output

[
{ :lineItemKey=>57, :sku=>"12GA-RIO",
:name=>"12 Gauge", :quantity=>5, :unitPrice=>5.76 },
{ :lineItemKey=>168, :sku=>"22LR-CAS-40-CPRN-50",
:name=>"22 Long", :quantity=>2, :unitPrice=>7.6 },
{ :lineItemKey=>236, :sku=>"300BLK-UNWD-CTC-220-BTHP-20",
:name=>"300 BLK", :quantity=>1, :unitPrice=>31.2 }
]

Ruby - Iterate over array of hashes and sum/compact the values of duplicate keys

What's wrong with a straightforward iteration through all the items?

arr = [{"94838"=>30.0}, {"94916"=>2.0}, {"94916"=>10.0}]

hsh = Hash.new(0) # Default value for each key is zero

# For each hash in arr, iterate through each key/value pair and
# increment the destination total associated with the key by the
# current value. Can use increment because of the zero default.
arr.each { |h| h.each { |k, v| hsh[k] += v } }
p hsh # Produces {"94838"=>30.0, "94916"=>12.0} as desired


Related Topics



Leave a reply



Submit