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 betest.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 h
with 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
Ruby: Any Gems for Threadpooling
Ssl_Connect Returned=1 Errno=0 State=Sslv3 Read Server Certificate B: Certificate Verify Failed MAC
Ruby Error After Installing Heroku Toolbelt
Scoping Date Attribute for This Week
Parsing Xls Spreadsheet in Rails Using Roo Gem
Start and Call Ruby Http Server in the Same Script
How to Click a Link in a Table Based on the Text in a Row
Ruby Optionparser Empty Switch "-" Behavior
Get All Keys in Hash with Same Value
Should I Be Using Rails or Ruby for This Website Application? How
Group Users by Age Range in Ruby
Ruby: Why Does '#Hash' Need to Overridden Whenever '#Eql' Is Overridden