2

If I have an array

apple_array = [
  #<name: "Bob", apples_eaten: 3>,
  #<name: "Robert", apples_eaten: 7>,
  #<name: "Bob", apples_eaten: 5>,
  #<name: "Rebecca", apples_eaten: 2>,
  #<name: "Robert", apples_eaten: 3>
]

how do I return something along the lines of the following, which goes through the array and calculates the total based on one of the attributes in the array?

name: Bob, apples_eaten: 8
name: Robert, apples_eaten: 10
name: Rebecca, apples_eaten: 2

Right now I know how to return the total amount of apples_eaten by calling apple_array.inject(0) { |sum, e| sum + e.apples_eaten }, Is there a way to map that line to each individual name?

4 Answers 4

1

Throw them all into a new hash that maps names => individual_total:

totals = Hash.new(0)
apple_array.each do |a|
  totals[a.name] += a.apples_eaten
end
Sign up to request clarification or add additional context in comments.

2 Comments

thanks! this is exactly what I'm looking for. I'll accept it as soon as SO lets me. By any chance, can you explain how the code might change if there is an an additional pears_eaten per person? I tried the following totals[a.name][a.apples_eaten] += a.apples_eaten totals[a.name][a.pears_eaten] += a.pears_eaten but that does not seem to be correct
Make an apples_total hash and a pears_total hash, perhaps? Then you'd just sum them each on their own line with apple_totals[a.name] += a.apples_eaten then the next line pear_totals[a.name] += a.pears_eaten. Or keep it in a hash of hashes with totals["pears"][a.name] += a.pears_eaten.
1
apple_array.inject(Hash.new 0) { |sum, e| sum[e.name] += e.apples_eaten;sum }

Comments

0

With multiple attributes:

# list the fruit
attributes = %i(apples_eaten pears_eaten)
# define the struct
Meal = Struct.new(:name, *attributes)
# create sample data
apple_array = [Meal.new("bob", 1, 2), Meal.new("marge", 5, 4), Meal.new("gomer", 7, 1), Meal.new("bob", 5, 0), Meal.new("gomer", 3, 9)]

totals = apple_array.inject(
  # start from a hash that defaults to empty stomach
  Hash.new { |h, k| h[k] = Meal.new(k, 0, 0) }
) { |r, e|
  # then for each value in apple_array, add each attribute to the total
  attributes.each do |a|
    r[e.name][a] += e[a]
  end
  # mustn't forget to return the accumulator
  r
# hash was just for easy lookup, but we want an array, so...
}.values

require 'pp'
pp totals
# => [#<struct Meal name="bob", apples_eaten=6, pears_eaten=2>,
#     #<struct Meal name="marge", apples_eaten=5, pears_eaten=4>,
#     #<struct Meal name="gomer", apples_eaten=10, pears_eaten=10>]

Comments

0

Here's my solution. It sorts the array by :name, then partitions the array into sub-arrays where each item has the same :name, then reduces the sub-arrays to a single item which sums :apples_eaten. It should be obvious how to sum other attributes as well...

apple_array = apple_array
  .sort_by { |a| a[:name] }
  .slice_when { |a, b| a[:name] != b[:name] }
  .map do |l|
    l.reduce do |a, b|
      a[:apples_eaten] += b[:apples_eaten]
      a
    end
  end

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.