Reputation:
I have a transactions array, which contains transaction hashes. I’m wanting to loop through the array of transaction hashes, and for each financial year, sum the interest, and principal values. These then need to be added to the respective arrays, grouped by financial year, as shown below.
In short:
For each financial year:
1.Sum the values of the interest keys
2.Create a hash
3.Add the hash to the respective array
4.Repeat for the principal values
I'm a little stuck on the very first step: For each financial year, do x - Any help would be appreciated.
Ruby 2.3.1
Given
@transactions = [
{"interest" => "120.00", "principal" => "250", "financial_year" => "2017"},
{"interest" => "120.00", "principal" => "250", "financial_year" => "2017"},
{"interest" => "120.00", "principal" => "250", "financial_year" => "2017"},
{"interest" => "120.00", "principal" => "250", "financial_year" => "2018"},
{"interest" => "120.00", "principal" => "250", "financial_year" => "2018"},
{"interest" => "120.00", "principal" => "250", "financial_year" => "2019"},
{"interest" => "120.00", "principal" => "250", "financial_year" => "2019"}
]
I wish to compute
@interest_totals = [
{"financial_year" => "2017", "total" => "360"}
{"financial_year" => "2018", "total" => "240"}
{"financial_year" => "2019", "total" => "240"}
]
and
@principal_totals = [
{"financial_year" => "2017", "total" => "750"}
{"financial_year" => "2018", "total" => "500"}
{"financial_year" => "2019", "total" => "500"}
]
Upvotes: 1
Views: 160
Reputation: 110685
I suggest you first compute the following hash.
h = @transactions.each_with_object({}) do |g,h|
h.update(g["financial_year"]=>g.reject { |k,_| k=="financial_year" }) do |_,o,n|
o.merge(n) { |_,oo,nn| (oo.to_i + nn.to_i).to_s }
end
end
#=> {"2017"=>{"interest"=>"360", "principal"=>"750"},
# "2018"=>{"interest"=>"240", "principal"=>"500"},
# "2019"=>{"interest"=>"240", "principal"=>"500"}}
This uses the form of Hash#update (aka merge!
) and Hash#merge that employ a block to determine the values of keys that are present in both hashes being merged. See the docs for details, especially the values of the block variables, _
, o
, n
, k
, oo
and nn
. (In the first block I've used an underscore for the common key to signify that it is not used in the block calculation.)
One advantage of this intermediate solution is that it needn't be changed if keys (other than "financial_year") are added ("balance", say), deleted or renamed.
You can now easily construct other objects. For example,
@interest_totals = h.map { |year,v| {"financial_year"=>year, "total"=> v["interest"] } }
#=> [{"financial_year"=>"2017", "total"=>"360"},
# {"financial_year"=>"2018", "total"=>"240"},
# {"financial_year"=>"2019", "total"=>"240"}]
Upvotes: 0
Reputation: 29599
Use inject
or reduce
or each_with_object
. I used each_with_object
below so that I can calculate the sum for both in one iteration
interests, principals = @transactions.each_with_object([Hash.new(0), Hash.new(0)]) do |transaction, (interests, principals)|
interests[transaction['financial_year']] += transaction['interest'].to_d
principals[transaction['financial_year']] += transaction['principal'].to_d
end
p interests
# {"2017"=>#<BigDecimal:7fd1c6cc20f0,'0.36E3',9(18)>, "2018"=>#<BigDecimal:7fd1c6cc1d30,'0.24E3',9(18)>, "2019"=>#<BigDecimal:7fd1c6cc1970,'0.24E3',9(18)>}
p principals
# {"2017"=>#<BigDecimal:7fd1c6cc2050,'0.75E3',9(18)>, "2018"=>#<BigDecimal:7fd1c6cc1c90,'0.5E3',9(18)>, "2019"=>#<BigDecimal:7fd1c6cc18d0,'0.5E3',9(18)>}
This will give you a hash where the year is the key and the value is the total. If you really need an array of hashes, use zip
@interest_sum = interests.map do |interest|
Hash[['financial_year', 'total'].zip(interest)]
end
Upvotes: 2
Reputation: 23671
You can group the values by year and then sum the principle and interest amount
@interest_totals = @transactions.group_by{|a| a["financial_year"]}.map{|k, v| {"financial_year" => k, "total" => v.map{|e|e["interest"].to_i}.sum}}
#=> [{ "financial_year" => "2017", "total" => 360 },
#=> { "financial_year" => "2018", "total" => 240 },
#=> { "financial_year" => "2019", "total" => 240 }]
@principal_totals = @transactions.group_by{|a| a["financial_year"]}.map{|k, v| {"financial_year" => k, "total" => v.map{|e|e["principal"].to_i}.sum}}
#=> [{ "financial_year" => "2017", "total" => 750 },
#=> { "financial_year" => "2018", "total" => 500 },
#=> { "financial_year" => "2019", "total" => 500 }]
Upvotes: 0