I'm learning about Underscore.js and the higher order functions. I came across the following problem, and I'm just simply stuck.
I'm not looking for a handout but at the same time, I don't have anywhere to turn for guidance in this. I would love someone to at least point me in the right direction.
What exactly does it mean by imperative vs functional?
I don't even know where to start when it comes to completing the 'functional' solution.
Any helpful info on this subject would be so much appreciated.
Please note: I do understand what map(), flatten(), and reduce() do. I just don't know how to apply it to this situation.
I do NOT understand what 'functional' really means. I would love any insight.
var 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,
},
];
// Underscore.js
// Count the ingredient occurence
// IMPERATIVE example:
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);
// FUNCTIONAL:
/* chain() together map(), flatten() and reduce() */
var ingredientCount = { "{ingredient name}": 0 };
// ? ? ?
expect(ingredientCount['mushrooms']).toBe(2);
You need to break it down into parts. First of all, you only care about the ingredients, but products
contains a lot of extra information, so use map
to just extract the ingredients.
Now you have ingredients in an array of arrays, but you just want one big array with all the ingredients, so use flatten
to give you that.
Now you want to count the ingredients. You can think of this as a series of reductions, where you have an object with the previous counts, and for this current item, you want to increment the count. This can be done with a reduce
that starts with an empty object. I'll leave the implementation of the incrementCount
function as an exercise for the reader.
const ingredients = _.map(products, p => p.ingredients);
const flatIngredients = _.flatten(ingredients);
const counts = _.reduce(flatIngredients, incrementCount, {});
With functional code, think of your data as passing through a series of transformations, each one bringing you closer to your goal, rather than one big loop you try to cram all your mutations into.