Jon
Jon

Reputation: 40032

Using underscore.js to increment a value based on number of times item seen in array

I am trying to learn JS better.

I have the below code which I am trying to put into underscore.js but am failing.

I'm hoping you can point out where I'm going wrong.

I'm trying to take a loop that I know works and then use underscores capabilites to refine it. The top test shows the loops, the second test is my attempt to do the same with underscore.js. I'm failing miserably!

Thanks

 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 }
    ];


it("should count the ingredient occurrence (imperative)", function () {
    var ingredientCount = { "{ingredient name}": 0 };

    for (i = 0; i < products.length; i+=1) {
        for (j = 0; j < products[i].ingredients.length; j+=1) {
            ingredientCount[products[i].ingredients[j]] = (ingredientCount[products[i].ingredients[j]] || 0) + 1;
        }
    }

    expect(ingredientCount['mushrooms']).toBe(2);
  });

  it("should count the ingredient occurrence (functional)", function () {
    var ingredientCount = { "{ingredient name}": 0 };


    var ffd = _(products).chain()
              .map(function(x){return x.ingredients;})
              .flatten()
              .reduce(function(memo,x){
                if (x===memo) 
                  {
                    return ingredientCount[memo] = ingredientCount[memo]+1;
                  }
                  else
                  {
                    return ingredientCount[memo] = 0;
                  }

                  })
              .value();

        /* chain() together map(), flatten() and reduce() */

    expect(ingredientCount['mushrooms']).toBe(2);
  });

Upvotes: 1

Views: 2249

Answers (2)

itur
itur

Reputation: 99

I'd use simpler to understand code, though it is not functional:

it("should count the ingredient occurrence (functional)", function () {

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

  var dummy = _(products).chain()
     .pluck("ingredients")
     .flatten()
     .each(function(x) { ingredientCount[x] = (ingredientCount[x] || 0) + 1; })
     .value();

  expect(ingredientCount['mushrooms']).toBe(2);
});

Upvotes: 0

Bergi
Bergi

Reputation: 664484

Your reduce is not very functional. Have a look at its documentation! The "memo", also know as "accumulator", is the value returned from the previous iteration - that should be your ingredientCount map.

var ingredientCount = _.chain(products)
  .pluck("ingredients") // don't use map for that
  .flatten()
  .reduce(function (memo, item) {
       memo[item] = (memo[item] || 0)+1;
       /* alternative:
       if (item in memo)
           memo[item]++;
       else
           memo[item] = 1;
       */
       return memo; // to be used in the next iteration!
  }, {/*"{ingredient name}": 0*/})
  .value();

Notice that memo === ingredientCount!

Upvotes: 3

Related Questions