Tokant
Tokant

Reputation: 324

Vanilla JavaScript: Filter nested array by array

Consider the below array of posts:

posts_array =
[
 {
  id: 1,
  categories: [
   {
    category_id: 1,
    category_name: "one"
   },
   {
    category_id: 2,
    category_name: "two"
   },
 },
 {
  id: 2,
  categories: [
   {
    category_id: 1,
    category_name: "one"
   },
   {
    category_id: 3,
    category_name: "three"
   },
 },
 {
  id: 3,
  categories: [
   {
    category_id: 1,
    category_name: "one"
   },
   {
    category_id: 4,
    category_name: "four"
   },
   {
    category_id: 5,
    category_name: "five"
   },
 }    
]

I then have an array of category ids which I want to filter the above array with:

const category_id_array = [1, 4];

I would like to filter so that all conditions are met, i.e. return elements in posts_array that include both categories.category_id = 1 and categories.category_id = 4

I have tried with the below (with the .some function) but it returns elements where either condition is met, i.e. either categories.category_id = 1 or categories.category_id = 4

function filterPostsByCategoryIds(category_id_array){
    const filter_categories = category_id_array;
    const custom_filter = d => d.categories.some(c => filter_categories.includes(c.category_id));
    const filtered_posts_array = posts_array.filter(custom_filter);
    return filtered_posts_array;
}

Swapping .some with .every does not return any elements at all.

How can I filter posts_array by category_id_array, so that it returns the last element in the above example array, i.e. where categories include all category_id's from category_id_array?

Thanks and stay safe!

Upvotes: 0

Views: 156

Answers (1)

Nick Parsons
Nick Parsons

Reputation: 50954

You can use .every() on your category_id_array and return true when there is .some() object within the current .categories which has an category_id equal to that of the current id (c) from your category_id_array:

const posts_array = [{ id: 1, categories: [{ category_id: 1, category_name: "one" }, { category_id: 2, category_name: "two" } ], }, { id: 2, categories: [{ category_id: 1, category_name: "one" }, { category_id: 3, category_name: "three" } ], }, { id: 3, categories: [{ category_id: 1, category_name: "one" }, { category_id: 4, category_name: "four" }, { category_id: 5, category_name: "five" } ], }];

function filterPostsByCategoryIds(category_id_array) {
  const custom_filter = d => category_id_array.every(c => d.categories.some(o => o.category_id === c));
  const filtered_posts_array = posts_array.filter(custom_filter);
  return filtered_posts_array;
}

const res = filterPostsByCategoryIds([1, 4]);
console.log(res);

You could also create a set of category_id for each object and then use .every() with .has() to check that the categories array has every id from the category_id like so:

const posts_array = [{ id: 1, categories: [{ category_id: 1, category_name: "one" }, { category_id: 2, category_name: "two" } ], }, { id: 2, categories: [{ category_id: 1, category_name: "one" }, { category_id: 3, category_name: "three" } ], }, { id: 3, categories: [{ category_id: 1, category_name: "one" }, { category_id: 4, category_name: "four" }, { category_id: 5, category_name: "five" } ], }];

function filterPostsByCategoryIds(category_id_array) {
  return posts_array.filter(({categories}) => {
    const cat_ids = new Set(categories.map(({category_id}) => category_id));
    return category_id_array.every(id => cat_ids.has(id));
  });
}

const res = filterPostsByCategoryIds([1, 4]);
console.log(res);

Upvotes: 1

Related Questions