1

I have an array of objects called results,

Every object has a property called ingredients, which is a large string containing many ingredients,

I need to get the 5 most used occurrences,

For example with this array of objects :

"results": [
    {
      "title": "Monterey Turkey Omelet",
      "href": "http://allrecipes.com/Recipe/Monterey-Turkey-Omelet/Detail.aspx",
      "ingredients": "butter, eggs, garlic, green pepper, monterey jack cheese, onions, turkey, water",
      "thumbnail": "http://img.recipepuppy.com/5506.jpg"
    },
    {
      "title": "Canadian Bacon Omelet",
      "href": "http://www.recipezaar.com/Canadian-Bacon-Omelet-309202",
      "ingredients": "butter, canadian bacon, cheddar cheese, eggs, garlic, onions, potato, red pepper, sour cream",
      "thumbnail": ""
    }]

I need to get butter (2), garlic (2), eggs (2), onions (2), turkey (1)

3
  • Pls share what you have tried Commented Sep 2, 2020 at 14:14
  • 1
    @Hemant Halwai for now I just did results.map((r) => r.ingredients).join(",")) which allows me to get a big big string of all ingredients separed by a , Now I need to count all occurences and get the 5 most named Commented Sep 2, 2020 at 14:16
  • @HemantHalwai last update : results.map((r) => r.ingredients).join(",").split(","), now I have an array with all ingredients but I just need to keep the 5 most named ! Commented Sep 2, 2020 at 14:18

6 Answers 6

2

Array.prototype.flatMap (MDN) is a good tool for creating aggregates over multiple objects.

const results = [
{
  "title": "Monterey Turkey Omelet",
  "href": "http://allrecipes.com/Recipe/Monterey-Turkey-Omelet/Detail.aspx",
  "ingredients": "butter, eggs, garlic, green pepper, monterey jack cheese, onions, turkey, water",
  "thumbnail": "http://img.recipepuppy.com/5506.jpg"
},
{
  "title": "Canadian Bacon Omelet",
  "href": "http://www.recipezaar.com/Canadian-Bacon-Omelet-309202",
  "ingredients": "butter, canadian bacon, cheddar cheese, eggs, garlic, onions, potato, red pepper, sour cream",
  "thumbnail": ""
}]

const wordCountMapReducer = function (wordCountMap, word) {
  if (wordCountMap.has(word)) {
    wordCountMap.set(word, wordCountMap.get(word) + 1)
  } else {
    wordCountMap.set(word, 1)
  }
  return wordCountMap
}

const countIngredients = results => [...results
  .flatMap(item => item.ingredients.split(', '))
  .reduce(wordCountMapReducer, new Map())
  .entries()
].sort((a, b) => b[1] - a[1])
  .slice(0, 5)
  .map(([word, count]) => `${word} (${count})`)
  .join(', ')

console.log(
  countIngredients(results),
) // butter (2), eggs (2), garlic (2), onions (2), green pepper (1)

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

Comments

2

It would probably be something like that:

const result = {
  "results": [
    {
      "title": "Monterey Turkey Omelet",
      "href": "http://allrecipes.com/Recipe/Monterey-Turkey-Omelet/Detail.aspx",
      "ingredients": "butter, eggs, garlic, green pepper, monterey jack cheese, onions, turkey, water",
      "thumbnail": "http://img.recipepuppy.com/5506.jpg"
    },
    {
      "title": "Canadian Bacon Omelet",
      "href": "http://www.recipezaar.com/Canadian-Bacon-Omelet-309202",
      "ingredients": "butter, canadian bacon, cheddar cheese, eggs, garlic, onions, potato, red pepper, sour cream",
      "thumbnail": ""
    }]
};

const frequency = {};
result.results.forEach( result => {
  result.ingredients.split(", ").forEach( ingred => {
    if (!frequency[ingred]) {
      frequency[ingred] = 1;
    } else {
      frequency[ingred] += 1;
    }
  });
});
const topKeys = Object.keys(frequency).sort((a,b) => frequency[b]-frequency[a]).slice(0, 5);
topKeys.forEach( key => {
   console.log(key, frequency[key]);
});

Iterate over each item of your results array, split the ingredients and increment and frequency object according to what you find inside. Then, sort the object key from bigger to smaller, take. the 5 first keys and retrieve their frequency from frequency.

Comments

1

Try this :

let temp1 = results.map((r) => r.ingredients).join(",")).reduce((acc,curr)=>{
    
    curr.forEach(ing=>{ 
        acc[ing]?acc[ing]++:acc[ing]=1;
    })
    return acc;
},{})

that gives you an object like :

{
  "butter": 2,
  "eggs": 2,
  "garlic": 2,
  "green pepper": 1,
  "monterey jack cheese": 1,
  "onions": 2,
  "turkey": 1,
  "water": 1,
  "canadian bacon": 1,
  "cheddar cheese": 1,
  "potato": 1,
  "red pepper": 1,
  "sour cream": 1
}

then sort it like :

var sortable = [];
for (var ing in temp1) {
    sortable.push([ing, temp1[ing]]);
}

let final= sortable.sort(function(a, b) {
    return b[1] - a[1];
}).slice(0,5)

let results = [{
    "title": "Monterey Turkey Omelet",
    "href": "http://allrecipes.com/Recipe/Monterey-Turkey-Omelet/Detail.aspx",
    "ingredients": "butter, eggs, garlic, green pepper, monterey jack cheese, onions, turkey, water",
    "thumbnail": "http://img.recipepuppy.com/5506.jpg"
  },
  {
    "title": "Canadian Bacon Omelet",
    "href": "http://www.recipezaar.com/Canadian-Bacon-Omelet-309202",
    "ingredients": "butter, canadian bacon, cheddar cheese, eggs, garlic, onions, potato, red pepper, sour cream",
    "thumbnail": ""
  }
]

let temp1 = results.map(res=>res.ingredients.split(', ')).reduce((acc, curr) => {

  curr.forEach(ing => {
    acc[ing] ? acc[ing]++ : acc[ing] = 1;
  })
  return acc;
}, {})


var sortable = [];
for (var ing in temp1) {
  sortable.push([ing, temp1[ing]]);
}

let final = sortable.sort(function(a, b) {
  return b[1] - a[1];
}).slice(0, 5);

console.log(final)

Comments

1

You can try this

var results = [
    {
      "title": "Monterey Turkey Omelet",
      "href": "http://allrecipes.com/Recipe/Monterey-Turkey-Omelet/Detail.aspx",
      "ingredients": "butter, eggs, garlic, green pepper, monterey jack cheese, onions, turkey, water",
      "thumbnail": "http://img.recipepuppy.com/5506.jpg"
    },
    {
      "title": "Canadian Bacon Omelet",
      "href": "http://www.recipezaar.com/Canadian-Bacon-Omelet-309202",
      "ingredients": "butter, canadian bacon, cheddar cheese, eggs, garlic, onions, potato, red pepper, sour cream",
      "thumbnail": ""
    }];
    var ingredients = [];
    for (index = 0; index < results.length; ++index) {
        ingredients.push(results[index]['ingredients'].split(','));
        
    }
    var merged = [].concat(...ingredients);
    var counts = {}; 
    merged.forEach(function(x) { counts[x] = (counts[x] || 0)+1; });
    console.log(counts);

JSFiddle here

Comments

1

Another not so efficient solution may be

let count = {}
results.forEach(obj => {
// you would need ingredients to be converted to an array
// you can use .join() here
    obj.ingredients.forEach(ingredient => {
        count[ingredient] = (count[ingredient] || 0) + 1
    }
}

Easier to understand, but it's a nested loop, so not too efficient. Maybe if your arrays are small these solution can be handy.

Comments

1

I came up with such code:

const results = [
    {
      "title": "Monterey Turkey Omelet",
      "href": "http://allrecipes.com/Recipe/Monterey-Turkey-Omelet/Detail.aspx",
      "ingredients": "butter, eggs, garlic, green pepper, monterey jack cheese, onions, turkey, water",
      "thumbnail": "http://img.recipepuppy.com/5506.jpg"
    },
    {
      "title": "Canadian Bacon Omelet",
      "href": "http://www.recipezaar.com/Canadian-Bacon-Omelet-309202",
      "ingredients": "butter, canadian bacon, cheddar cheese, eggs, garlic, onions, potato, red pepper, sour cream",
      "thumbnail": ""
    }];

const topIngridients = (data, top) => {
    const ingridients = Object.entries(data.reduce(
        (collector, item) => {
            return item.ingredients
                .split(',')
                .map(ing => ing.trim())
                .reduce((collector, ingredient) => {
                    collector[ingredient] = (collector[ingredient] || 0) + 1;
                    return collector;
                }, collector)
        }, 
        {}));
    ingridients.sort((a,b) => b[1] - a[1]);
    return ingridients.slice(0, top);
}

console.log(topIngridients(results, 5));

1 Comment

No problem. Anyway, I like the solution with flatMap more, because it reduces the calls chaining, so the code becomes more readable.

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.