hancho
hancho

Reputation: 1437

Ruby -- Adding the values of sub arrays in an array by their key

So I know how to add all the values in an array.

Example, the sum of [1,2,3,4]...

[1,2,3,4].inject(&:+)
#=> 10 

However, I have an array of arrays and would like to add the values that have the same first element of each sub array.

# example
[["A", 10],["A", 5],["B", 5],["B", 5],["C", 15], ["C", 15]]

Desired output:

"(A : 15) - (B : 10) - (C : 30)"

Any help would be appreciated!

Upvotes: 2

Views: 1218

Answers (4)

holaymolay
holaymolay

Reputation: 580

There are more elegant ways of doing this, but here is the solution as a block, so you can understand the logic...

What this does is :

  1. convert the array to a hash, when combining values.
  2. Then it builds the string, one element at a time, storing each in an array.
  3. And finally, it combines the array of strings into your desired output.

'

my_array = [["A", 10],["A", 5],["B", 5],["B", 5],["C", 15],["C", 15]]
my_hash = {}
output_array = []

my_array.each do |item|
  my_hash[item[0]] ||= 0
  my_hash[item[0]] += item[1]
end

my_hash.each do |k,v|
  output_array.push("(#{k} : #{v})")
end

puts output_array.join(" - ")

Upvotes: 0

Andrey Deineko
Andrey Deineko

Reputation: 52357

a = [["A", 10],["A", 5],["B", 5],["B", 5],["C", 15], ["C", 15]]
result = a.group_by(&:first).each_with_object({}) do |(k, v), h|
  h[k] = v.map(&:last).inject(:+)
  # if your on Ruby 2.4+ you can write h[k] = v.sum(&:last)
end
#=> {"A"=>15, "B"=>10, "C"=>30}

Another option would be to build the hash from the beginning:

result = a.each_with_object({}) {|(k, v), h| h[k] = h[k].to_i + v }
#=> {"A"=>15, "B"=>10, "C"=>30}

If your desired output is literally a string "(A : 15) - (B : 10) - (C : 30)":

result.map { |k, v| "(#{k} : #{v})" }.join(' - ')
#=> "(A : 15) - (B : 10) - (C : 30)"

Upvotes: 2

akuhn
akuhn

Reputation: 27793

Try this

arr = [["A", 10],["A", 5],["B", 5],["B", 5],["C", 15], ["C", 15]]

arr.group_by(&:first).map { |key, group| [key, group.map(&:last).inject(:+)] } 
# => [["A", 15], ["B", 10], ["C", 30]]

How does this work?

  • group_by(&:first) groups the subarrays by first element
  • map { |key, group| ... } transforms the groups
  • group.map(&:last).inject(:+) sums up all last elements in a group

Upvotes: 2

Cary Swoveland
Cary Swoveland

Reputation: 110675

arr = [["A", 10],["A", 5],["B", 5],["B", 5],["C", 15], ["C", 15]]

h = arr.each_with_object(Hash.new(0)) { |(f,g),h| h[f] += g }
  #=> {"A"=>15, "B"=>10, "C"=>30} 

Then

h.map { |pair| "(%s : %s)" % pair }.join(" - ")
  #=> "(A : 15) - (B : 10) - (C : 30)"

which you can combine like so:

arr.each_with_object(Hash.new(0)) { |(f,g),h| h[f] += g }.
    map { |pair| "(%s : %s)" % pair }.join(" - ")

See Hash::new, especially with regards to the use of a default value (here 0).

Upvotes: 3

Related Questions