5

How can I filter an array with a deeply nested array? Given the following 2 arrays, I need the result to be an array with only the rice cakes and the gluten-free-pizza objects:

const foodsILike = ['gluten-free', 'carb-free', 'flavor-free'];
const foodsAvailable = [
  { name: 'pasta', tags: ['delicious', 'has carbs']}, 
  { name: 'gluten-free-pizza', tags: ['gluten-free']}, 
  { name: 'pizza', tags: ['delicious', 'best meal of the year']},
  { name: 'rice cakes', tags: ['flavor-free']}
]

I tried the following which just returns everything (the 4 objects):

var result = foodsAvailable.filter(function(food) {
  return foodsILike.filter(function(foodILike) {
    return foodILike === food;
  })
})

result
// Array [ Object, Object, Object, Object]
Simpleton
  • 6,285
  • 11
  • 53
  • 87

3 Answers3

13

You could use Array#some and Array#includes for checking if foodsILike contains a tag.

const foodsILike = ['gluten-free', 'carb-free', 'flavor-free'];
const foodsAvailable = [{ name: 'pasta', tags: ['delicious', 'has carbs'] }, { name: 'gluten-free-pizza', tags: ['gluten-free'] }, { name: 'pizza', tags: ['delicious', 'best meal of the year'] }, { name: 'rice cakes', tags: ['flavor-free'] }]

var result = foodsAvailable.filter(function(food) {
    return food.tags.some(function(tag) {
        return foodsILike.includes(tag);
    });
});

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • what's a more performant way to do this? If you were deeply nested with large arrays this could get computationally expensive. – zero_cool Aug 02 '18 at 23:32
  • 1
    @zero_cool, it depends on the structure and the wanted result, like all nodes to the found item or just the item of the nested structure. maybe it could be faseter with an object or map for `foodsILike`. for speeding, the array methods should be avoided if speed really matters. – Nina Scholz Aug 03 '18 at 05:58
  • @NinaScholz Could you take a look at a question I've posted: https://stackoverflow.com/questions/51663557/most-performant-way-to-sort-a-deeply-nested-array-of-objects-by-another-deeply-n This is related to my question above – zero_cool Aug 03 '18 at 16:13
  • 1
    This is great! For anyone finding this later and wanting it reformatted in ES6 like I did, it would be: ```const result = foodsAvailable.filter(food => food.tags.some(tag => foodsILike.includes(tag));``` – bildungsroman Nov 25 '20 at 22:20
2

You are missing a length check on your inner filter and you aren't checking the tags array for the value in that inner filter

const foodsILike = ['gluten-free', 'carb-free', 'flavor-free'];
const foodsAvailable = [
  { name: 'pasta', tags: ['delicious', 'has carbs']}, 
  { name: 'gluten-free-pizza', tags: ['gluten-free']}, 
  { name: 'pizza', tags: ['delicious', 'best meal of the year']},
  { name: 'rice cakes', tags: ['flavor-free']}
]

var result = foodsAvailable.filter(function(food) {
  return foodsILike.filter(function(foodILike) {
    // is value in tags array?
    return food.tags.indexOf(foodILike) >-1;
  }).length;// length acts as boolean, any length greater than zero is truthy
});

console.log(result)
charlietfl
  • 170,828
  • 13
  • 121
  • 150
0

You can use array.reduce to reduce your array into desired structure, by checking for the condition i.e. foodsILike contains the tag or not.

The benefit of using array.reduce is that you can modify the structure if you want in the reduce loop itself.

const foodsILike = ['gluten-free', 'carb-free', 'flavor-free'];
const foodsAvailable = [
  { name: 'pasta', tags: ['delicious', 'has carbs']}, 
  { name: 'gluten-free-pizza', tags: ['gluten-free']}, 
  { name: 'pizza', tags: ['delicious', 'best meal of the year']},
  { name: 'rice cakes', tags: ['flavor-free']}
];

let foods = foodsAvailable.reduce((foods, food) => {
  if(foodsILike.some(f => food.tags.indexOf(f) !== -1)) {
    foods.push(food);
  }
  return foods;
},[]);

console.log(foods);
Anurag Singh Bisht
  • 2,683
  • 4
  • 20
  • 26