5
\$\begingroup\$

I'm trying to group by keys in an Array of Hashes in the following form and add their totals. Maybe with #map and #reduce/#inject. I know there is a more compact way to do this.

Given I have

codes_and_totals = [{'1001' => 153, '212' => 153}, {'212' => 1}]

When I run

process_codes_and_totals(codes_and_totals)

Then the return value should be

=> {'1001' => 153, '212' => 154}

Currently #process_codes_and_totals looks like this

def process_codes_and_totals(codes_and_totals)
    totals_across_locations = {}
    codes_and_totals.each do |location|
      location.each do |class_code, total|
        totals_across_locations[class_code] ||= 0
        totals_across_locations[class_code] += total
      end
    end

    totals_across_locations
end
\$\endgroup\$
6
  • 1
    \$\begingroup\$ Please try to edit your title to reflect what your code is doing. Saying you want better code in Code Review is redundant. \$\endgroup\$
    – Marc-Andre
    Commented Aug 1, 2016 at 14:29
  • \$\begingroup\$ Thanks for the feedback @Marc-Andre, let me know if there is anything else I can do to make this clearer/better. \$\endgroup\$
    – David West
    Commented Aug 1, 2016 at 14:40
  • \$\begingroup\$ "Better", "more compact", "faster", etc. are all implied for questions on Code Review. The site standard is to simply state exactly what the code accomplishes. See How to Ask. \$\endgroup\$ Commented Aug 1, 2016 at 14:53
  • \$\begingroup\$ I have rolled back Rev 4 to 3. Please see What to do when someone answers. \$\endgroup\$ Commented Aug 1, 2016 at 17:55
  • \$\begingroup\$ As a note, some people recommend using #each_with_object and a blank hash as the argument. More info is here technology.customink.com/blog/2014/10/14/…. \$\endgroup\$
    – David West
    Commented Aug 1, 2016 at 22:54

1 Answer 1

5
\$\begingroup\$

One way might be to use #reduce (as you suggest) and Hash#merge with a block:

totals = codes_and_totals.reduce({}) do |sums, location|
  sums.merge(location) { |_, a, b| a + b }
end

The merge-block is only invoked if a key exists in both the hashes being merged, so we don't need to guard against nil or start with a zero value. If the block runs, it's because there are two number values that need to be merged (i.e. added, in this case).

\$\endgroup\$
3
  • \$\begingroup\$ What is the _ for as the first block argument to the merge? \$\endgroup\$
    – David West
    Commented Aug 1, 2016 at 14:31
  • 4
    \$\begingroup\$ _ is the conventional name for a "throw-away" variable whose value you don't care about. \$\endgroup\$ Commented Aug 1, 2016 at 14:32
  • \$\begingroup\$ Is there a way to do this for nested hashes? \$\endgroup\$
    – Kelseydh
    Commented Feb 28, 2023 at 2:22

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.