user4541806
user4541806

Reputation:

Ruby Array of Hashes - For each financial year

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

Answers (3)

Cary Swoveland
Cary Swoveland

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

jvnill
jvnill

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

Deepak Mahakale
Deepak Mahakale

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

Related Questions