1

A production has_many :production_lines, production_line belongs_to :item, item has_one :recipe, recipe has_many :recipe_lines, recipe_line belongs_to :item,

production_line and recipe line have attribute quantity. I need to group recipe_lines for a production by item, with quantity that equals to production_line.quantity * recipe_line.quantity

  def item_quantities
        array = production_lines.map do |p|
          p.item.recipe.recipe_lines.map do |r|
            {
              item_id: r.item_id,
              item_name: r.item.name,
              quantity: r.quantity * p.quantity
            }
          end
        end
        array.flatten(1).group_by { |p| p[:item_id] }
             .transform_values { |vals| vals.sum { |val| val[:quantity] } }
          end

This returns:

item_quantities = {
1: 10,
2: 5
}

where key is item_id and value is quantity. Values are correct.

However I would like to return:

item_quantities = [
{
id: 1,
name: "Tomato",
quantity: 10,
},
{
id: 2, 
name: "Carrot",
quantity: 5
}
]

How should I change my solution to achieve that?

1 Answer 1

4

First of all, your nested map followed by flatten(1) can be simplified by making the first map into flat_map. If you do this you could remove the flatten(1).

From this point your code is most of the way there, but you could make the following changes to get the desired output:

  1. you can group by multiple attributes, name and id. In another language you might use a tuple for this. Ruby doesn't have tuples, so we can just use a len-2 array:

    .group_by { |p| [p[:item_id], p[:item_name]] }
    .transform_values { |vals| vals.sum { |val| val[:quantity] } }
    
  2. At this point you have a hash mapping [id,name] tuple to quantity:

    { [1,"foo"] => 123, [2, "bar"] => 456 }
    

    and you can coerce this to the desired data type using reduce (or each_with_object, if you prefer):

    .reduce([]) do |memo, ((id, name), quantity)|
      memo << {
        id: id,
        name: name,
        quantity: quantity
      }
    end
    

The wierd looking ((id, name), quantity) is a kind of destructuring. See https://jsarbada.wordpress.com/2019/02/05/destructuring-with-ruby/ specifically the sections on "Destructuring Block Arguments" and "Destructuring Hashes".

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

5 Comments

I think there is an error in the point 1 where you do he group_by. A } missing I guess.
My bad, I had an extra { actually
Ok so in the group_by, changing p[:item_id] to [p.item_id] returns undefined method `item_id' for #<Hash....
Before grouping, I have an array of objects. Each object looks like { "item_id": "aadc79b9-ff50-4012-b2cb-0deee88138a9", "item_name": "Pumpkins", "quantity": "600.0" }
Yeah my bad @Catmal it was another error on my part, updated answer it should be [p[:item_id], p[:item_name]]

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.