0

I'm working through an exercise using the underscore library to perform a count of item occurrences.

Specifically, I have this array:

products = [
       { name: "Sonoma", ingredients: ["artichoke", "sundried tomatoes", "mushrooms"], containsNuts: false },
       { name: "Pizza Primavera", ingredients: ["roma", "sundried tomatoes", "goats cheese", "rosemary"], containsNuts: false },
       { name: "South Of The Border", ingredients: ["black beans", "jalapenos", "mushrooms"], containsNuts: false },
       { name: "Blue Moon", ingredients: ["blue cheese", "garlic", "walnuts"], containsNuts: true },
       { name: "Taste Of Athens", ingredients: ["spinach", "kalamata olives", "sesame seeds"], containsNuts: true }
    ];

And am trying to use map(), flatten() and reduce() to determine the number of times each ingredient shows up.

When I use the code below, ingredientCount returns undefined values:

var ingredientCount = { "{ingredient name}": 0 };

    _(products).chain()
    .map(function(obj) {return obj.ingredients;})
    .flatten().reduce(function(memo, x) {
      return memo[x] = (memo[x] || 0) + 1;
    }, ingredientCount)
    .value();

However, if I remove the second argument to reduce() and include the initial object in the function body, everything works as expected, i.e.:

var ingredientCount = { "{ingredient name}": 0 };

_(products).chain()
.map(function(obj) {return obj.ingredients;})
.flatten().reduce(function(memo, x) {
  return ingredientCount[x] = (ingredientCount[x] || 0) + 1;
})
.value();

Can somebody help explain why this is the case?

Thanks!

3
  • I don't understand why you are using reduce...the reduce function will return asingle value what you want is teh sum of all ingredients individually something like this "{"artichoke":1,"sundried tomatoes":2,"mushrooms":2,"roma":1,"goats cheese":1,"rosemary":1,"black beans":1,"jalapenos":1,"blue cheese":1,"garlic":1,"walnuts":1,"spinach":1,"kalamata olives":1,"sesame seeds":1}" Commented Oct 17, 2015 at 0:39
  • @Cyril—the accumulator can be an Object (i.e. the value can be an object reference). Commented Oct 17, 2015 at 0:49
  • Yes you are right but when i applied Jaromanda answer to the above problem i got a single result...not what is expected..your answer works fine. Commented Oct 17, 2015 at 0:56

3 Answers 3

1

return memo[x] = (memo[x] || 0) + 1; returns a number for the next iteration of the loop to use as memo.

instead do

memo[x] = (memo[x] || 0) + 1;
return memo;

if you really want to do it in a single statement

return memo[x] = (memo[x] || 0) + 1, memo;
Sign up to request clarification or add additional context in comments.

2 Comments

This is the second question I've seen today where someone made essentially the same mistake with reduce.
I think there's some sort of mass shared consciousness thing happening on SO - you see clusters of almost the same problem come up occasionally. the batch of fizzbuzz and tic-tac-toe questions is almost due :p
1

Always interesting to see a plain JS alternative:

var counts = p.reduce(function(acc, prod) {
  prod.ingredients.forEach(function(i) {
    acc[i] = (acc[i] || 0) + 1;
  });
  return acc;
},{});

which seems lot clearer, though in a general case it's better to use:

  if (!acc.hasOwnProperty(i)) acc[i] = 0;
  acc[i]++

for the counter part.

products = [
       { name: "Sonoma", ingredients: ["artichoke", "sundried tomatoes", "mushrooms"], containsNuts: false },
       { name: "Pizza Primavera", ingredients: ["roma", "sundried tomatoes", "goats cheese", "rosemary"], containsNuts: false },
       { name: "South Of The Border", ingredients: ["black beans", "jalapenos", "mushrooms"], containsNuts: false },
       { name: "Blue Moon", ingredients: ["blue cheese", "garlic", "walnuts"], containsNuts: true },
       { name: "Taste Of Athens", ingredients: ["spinach", "kalamata olives", "sesame seeds"], containsNuts: true }
    ];
    
function countIngredients(p) {
  var counts = p.reduce(function(acc, prod) {
    prod.ingredients.forEach(function(i) {
      acc[i] = (acc[i] || 0) +1;
    })
    return acc;
  },{})
  return counts;
}

document.write(JSON.stringify(countIngredients(products)));

Comments

0

I would do it like this in underscore

    products = [
           { name: "Sonoma", ingredients: ["artichoke", "sundried tomatoes", "mushrooms"], containsNuts: false },
           { name: "Pizza Primavera", ingredients: ["roma", "sundried tomatoes", "goats cheese", "rosemary"], containsNuts: false },
           { name: "South Of The Border", ingredients: ["black beans", "jalapenos", "mushrooms"], containsNuts: false },
           { name: "Blue Moon", ingredients: ["blue cheese", "garlic", "walnuts"], containsNuts: true },
           { name: "Taste Of Athens", ingredients: ["spinach", "kalamata olives", "sesame seeds"], containsNuts: true }
        ];
    ingredientCount = {};

    _(products).chain()
        .map(function(obj) {return obj.ingredients;}).flatten().each(function(memo, x) {
       ingredientCount[memo] = (ingredientCount[memo] || 0) + 1;
    });

    document.write(JSON.stringify(ingredientCount));

    //this will produce this "{"artichoke":1,"sundried tomatoes":2,"mushrooms":2,"roma":1,"goats cheese":1,"rosemary":1,"black beans":1,"jalapenos":1,"blue cheese":1,"garlic":1,"walnuts":1,"spinach":1,"kalamata olives":1,"sesame seeds":1}"
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script>

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.