2

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!

4 Answers 4

3
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).

Sign up to request clarification or add additional context in comments.

1 Comment

default value of 0 is cleaner than my solution with checking for key presence - good one, as usual!
2

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

1 Comment

in 2.4.0 to sum up the elements in a group we can also use sum like this: group.sum(&:last).
2
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)"

2 Comments

re your first example, in 2.4.0 we can also do h[k] = v.sum(&:last)
@sagarpandya82 yep, I knew someone would raise it :) edited :)
0

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(" - ")

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.