4
\$\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 Aug 1 '16 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 Aug 1 '16 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\$ – 200_success Aug 1 '16 at 14:53
  • \$\begingroup\$ I have rolled back Rev 4 to 3. Please see What to do when someone answers. \$\endgroup\$ – 200_success Aug 1 '16 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 Aug 1 '16 at 22:54
4
\$\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\$
2
  • \$\begingroup\$ What is the _ for as the first block argument to the merge? \$\endgroup\$ – David West Aug 1 '16 at 14:31
  • 3
    \$\begingroup\$ _ is the conventional name for a "throw-away" variable whose value you don't care about. \$\endgroup\$ – 200_success Aug 1 '16 at 14:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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