0

I've written a small application that has an array of recipes. Each recipe then has a child array of ingredients. What I would like to do is iterate through all of the ingredients in all of the recipes and pull out the unique ingredients.

At the moment I have a function that iterates through all ingredients, from all recipes, and then adds an empty version of that ingredient to the set. This should work but when I log the result, it doesn't make sense because ingredients added to the set are all populated - they're not using the empty objects that I created. Because of the nested properties being different, this means the set is adding the ingredient multiple times because the objects aren't equal.

I was then re-looping through the ingredients afterwards to calculate the values, but this should only be happening after the logger entry. So I don't understand how the values are being populated before that functionality is called?

This is what I have currently:

calculate(recipes: Recipe[]): Observable<Calculation[]> {

    let calculations: Calculation[] = [];
    let uniqueIngredients = new Set();

    for (let i = 0; i < recipes.length; i++) {
      for (let j = 0; j < recipes[i].ingredients.length; j++) {
        // First get all the unique ingredients        
        uniqueIngredients.add(this.getEmptyIngredient(recipes[i].ingredients[j]));
      }
    }

    console.log('unique ingredients', uniqueIngredients);

    let $this = this;
    uniqueIngredients.forEach(function (value) {
      let ingredientCalculation = value as Calculation;
      for (let i = 0; i < recipes.length; i++) {
        for (let j = 0; j < recipes[i].ingredients.length; j++) {
          if (ingredientCalculation.ingredient.definition.name === recipes[i].ingredients[j].definition.name) {
            // Calculation already exists so update it
            let calculation: Calculation = $this.calculateIngredient(recipes[i].ingredients[j], recipes[i].quantity);

            ingredientCalculation.quantityMeasurement += calculation.quantityMeasurement;
            ingredientCalculation.quantityUnits += calculation.quantityUnits;
            ingredientCalculation.totalCost += calculation.totalCost;
          }
        }
      }

      calculations.push(ingredientCalculation);
    });

    return of(calculations);
}

private getEmptyIngredient(ingredient: Ingredient): Calculation {
    let calculation: Calculation = {
      ingredient: ingredient,
      quantityUnits: 0,
      quantityMeasurement: 0,
      totalCost: 0
    };

    return calculation;
  }

When I look at the console, this is the result:

enter image description here

Judging by my code, I would have expected there to be only 1 instance of 'Apple' with all the properties set to 0, but this isn't the case.

Could anyone explain to me where I'm going wrong?

EDIT: I understand the comments about object equality, but what I don't understand is that the objects should be empty: At the time the console logs, the collect should contain empty objects - why are they all populated at that point when all of the calculation logic happens after the log?

alimac83
  • 2,346
  • 6
  • 34
  • 50
  • 1
    "*Could anyone explain to me where I'm going wrong?*" two different objects are always different: `console.log({foo:1} === {foo:1})` is `false`. [Why does Javascript Set not do unique objects?](https://stackoverflow.com/q/41404607) | [es6 unique array of objects with set](https://stackoverflow.com/q/39997067) – VLAZ Feb 22 '23 at 10:27
  • See also [How to get distinct values from an array of objects in JavaScript?](https://stackoverflow.com/q/15125920) – VLAZ Feb 22 '23 at 10:29

1 Answers1

0

As VLAZ said, object equality doesn't really work as you might assume. Two objects with identical properties are still unequal unless they reference the same object in memory.

Instead, does your use case provide some kind of ingredient ID? If so, you can use a Map and myMap.set(ingredientID, ingredientObjectReference). You'll wind up with only unique ingredients because duplicates will just reset the previously set key.

Tyler V.
  • 2,471
  • 21
  • 44