JavaScript: How to Group Array of Objects with Nested Properties? - javascript

I have this array of objects:
[
{
"date": "08/08/2022",
"name": "Swordsman",
"items": [
{
"item_id": 1,
"item": {
"item_name": "Item 1",
},
"count": 1
}
]
},
{
"date": "08/08/2022",
"name": "Swordsman",
"items": [
{
"item_id": 1,
"item": {
"item_name": "Item 1",
},
"count": 2
},
{
"item_id": 2,
"item": {
"item_name": "Item 2",
},
"count": 1
}
]
}
]
What I wanted to achieve is to group by date, name and the number of items. The desired result would be:
[
{
"date": "08/08/2022",
"name": "Swordsman",
"items": [
{
"item_id": 1,
"name": "Item 1",
"count": 3
},
{
"item_id": 2,
"name": "Item 2",
"count": 1
}
]
}
]
If it has the same date but different name like:
[
{
"date": "08/08/2022",
"name": "Swordsman",
"items": [
{
"item_id": 1,
"item": {
"item_name": "Item 1",
},
"count": 1
}
]
},
{
"date": "08/08/2022",
"name": "Swordsman",
"items": [
{
"item_id": 1,
"item": {
"item_name": "Item 1",
},
"count": 2
},
{
"item_id": 2,
"item": {
"item_name": "Item 2",
},
"count": 1
}
]
},
{
"date": "08/08/2022",
"name": "Archer",
"items": [
{
"item_id": 1,
"item": {
"item_name": "Item 1",
},
"count": 2
},
{
"item_id": 2,
"item": {
"item_name": "Item 2",
},
"count": 1
}
]
}
]
The desired result would be:
[
{
"date": "08/08/2022",
"name": "Swordsman",
"items": [
{
"item_id": 1,
"name": "Item 1",
"count": 3
},
{
"item_id": 2,
"name": "Item 2",
"count": 1
}
]
},
{
"date": "08/08/2022",
"name": "Archer",
"items": [
{
"item_id": 1,
"name": "Item 1",
"count": 2
},
{
"item_id": 2,
"name": "Item 2",
"count": 1
}
]
}
]
I've tried using lodash and array reduce method but still unable to get the desired result:
const groups = data.reduce((groups, data) => {
const date = data.date;
if (!groups[date]) {
groups[date] = [];
}
groups[date].push(data);
return groups;
}, {});
const groupArrays = Object.keys(groups).map((date) => {
return {
date,
items: groups[date]
};
});
How to achieve this one? Need your inputs. Thanks!

You can achieve this as:
const arr = [
{
date: '08/08/2022',
name: 'Swordsman',
items: [
{
item_id: 1,
item: {
item_name: 'Item 1',
},
count: 1,
},
],
},
{
date: '08/08/2022',
name: 'Swordsman',
items: [
{
item_id: 1,
item: {
item_name: 'Item 1',
},
count: 2,
},
{
item_id: 2,
item: {
item_name: 'Item 2',
},
count: 1,
},
],
},
{
date: '08/08/2022',
name: 'Archer',
items: [
{
item_id: 1,
item: {
item_name: 'Item 1',
},
count: 2,
},
{
item_id: 2,
item: {
item_name: 'Item 2',
},
count: 1,
},
],
},
];
function addItemsToNewArrFromOld(oldItems, newItemsToAdd) {
newItemsToAdd.forEach((o) => {
const { item_id, count, item: { item_name: item }} = o;
const itemInOldItems = oldItems.find((obj) => obj.item_id === o.item_id);
if (itemInOldItems) itemInOldItems.count += o.count;
else oldItems.push({ item_id, count, item });
});
}
const map = new Map();
arr.forEach(({ date, name, items }) => {
const key = `${date}-${name}`;
if (map.has(key)) {
const objForKeyFound = map.get(key);
addItemsToNewArrFromOld(objForKeyFound.items, items);
} else {
map.set(key, {
date, name, items: items.map(({ item_id, item, count }) => ({ item_id, count, item: item.item_name })),
});
}
});
const result = [...map.values()];
console.log(result);

Using Array#reduce, iterate over the list while updating a Map where the key is <date>-<name> and the value is the grouped object (having all items of this key).
Using Array#map, iterate over the above grouped objects. For each object, we need to group its items by item_id while updating the count.
const groupByDateAndName = arr => [...
arr.reduce((map, { date, name, items = [] }) => {
const key = `${date}-${name}`;
const { items: prevItems = [] } = map.get(key) ?? {};
map.set(
key,
{
date,
name,
items: [
...prevItems,
...items.map(({ item_id, item, count }) => ({ item_id, count, name: item.item_name }))
]
}
);
return map;
}, new Map)
.values()
];
const groupItemsById = arr => arr.map(e => {
const items = [...
e.items.reduce((map, { item_id, name, count }) => {
const { count: prevCount = 0 } = map.get(item_id) ?? {};
map.set(item_id, { item_id, name, count: prevCount + count })
return map;
}, new Map)
.values()
]
return { ...e, items };
});
const group = (arr = []) => {
const list = groupByDateAndName(arr);
return groupItemsById(list);
}
const arr = [
{
"date": "08/08/2022",
"name": "Swordsman",
"items": [
{ "item_id": 1, "item": { "item_name": "Item 1" }, "count": 1 }
]
},
{
"date": "08/08/2022",
"name": "Swordsman",
"items": [
{ "item_id": 1, "item": { "item_name": "Item 1" }, "count": 2 },
{ "item_id": 2, "item": { "item_name": "Item 2" }, "count": 1 }
]
},
{
"date": "08/08/2022",
"name": "Archer",
"items": [
{ "item_id": 1, "item": { "item_name": "Item 1" }, "count": 2 },
{ "item_id": 2, "item": { "item_name": "Item 2" }, "count": 1 }
]
}
];
console.log( group(arr) );

Related

Array filter returning entire array

I'm trying to filter an array with nested objects like this:
const items = [
{
"id": 1,
"name": "test1",
"subitems": [
{
"id": 2,
"name": "test2",
"subsubitems": [
{
"id": 3,
"name": "test3",
},
{
"id": 4,
"name": "test4",
}
]
}
]
},
{
"id": 10,
"name": "test10",
"subitems": [
{
"id": 20,
"name": "test20",
"subsubitems": [
{
"id": 30,
"name": "test30",
}
]
}
]
}
]
const filteredResults = items.filter((item) =>
item.subitems.filter((subitem) =>
subitem.subsubitems.filter((subsubitem) =>
subsubitem.name.includes('test3')
)
)
)
console.log(filteredResults)
But it's not filtering correctly, the original array is being returned. Right now I'm just attempting to filter at the subsubitems level. Ultimately I want to return an array with any matches to name at any level.
So test2 would return an array like this:
[
{
"id": 1,
"name": "test1",
"subitems": [
{
"id": 2,
"name": "test2",
"subsubitems": [
{
"id": 3,
"name": "test3",
},
{
"id": 4,
"name": "test4",
}
]
}
]
}
]
And test3 would return an array like this:
[
{
"id": 1,
"name": "test1",
"subitems": [
{
"id": 2,
"name": "test2",
"subsubitems": [
{
"id": 3,
"name": "test3",
}
]
}
]
}
]
And test would return everything.
Try This:
const items = [
{
id: 1,
name: "test1",
subitems: [
{
id: 2,
name: "test2",
subsubitems: [
{
id: 3,
name: "test3",
},
{
id: 4,
name: "test4",
},
],
},
],
},
{
id: 10,
name: "test10",
subitems: [
{
id: 20,
name: "test20",
subsubitems: [
{
id: 30,
name: "test30",
},
],
},
],
},
];
const filterItems = (items, filter) => {
const result = [];
items.forEach((item) => {
if (item.name === filter) {
result.push(item);
}
item.subitems.forEach((subitem) => {
if (subitem.name === filter) {
result.push(item);
}
subitem.subsubitems.forEach((subsubitem) => {
if (subsubitem.name === filter) {
result.push(item);
}
});
});
});
console.log("filtered", result);
};
filterItems(items, "test10");
Result is :
filtered [ { id: 10, name: 'test10', subitems: [ [Object] ] } ]

What is the best way to group this js array items in this structure

how can i convert returned data from this structure:
[
{
"id": 91,
"name": "Art",
"division": {
"id": 4,
"name": "1st level",
"created_at": "2018-11-05T10:11:37+00:00",
},
"created_at": "2018-11-05T10:11:37+00:00",
},
{
"id": 188,
"name": "English",
"division": {
"id": 9,
"name": "3rd level",
"created_at": "2018-11-05T10:11:37+00:00",
},
"created_at": "2018-11-05T10:11:37+00:00",
},
{
"id": 218,
"name": "Art",
"division": {
"id": 11,
"name": "3rd level",
"created_at": "2018-11-05T10:11:37+00:00",
},
"created_at": "2018-11-05T10:11:37+00:00",
}
]
to this structure :
[
{
"id": 1,
"name": "Art",
"classes": [
{
"id": 91,
"name": "1st level",
},
{
"id": 218,
"name": "3rd level",
},
],
},
{
"id": 2,
"name": "English",
"classes": [
{
"id": 188,
"name": "3rd level",
},
],
},
]
note: class.id = parentSubject.id
I wrote some codes to solve the problem but I'm looking for the best way !!
i use .reduce() function
I will attach the codes in the comments box.
Here is how I would do it:
let ans = initialArray.reduce((cum,x) => {
let foundIndex = cum.findIndex((a) =>{
return a.name == x.name});
if(foundIndex!=-1){
cum[foundIndex].classes.push({
id : x.id,
name : x.division.name
})
}
else{
cum.push({
id : cum.length+1,
name : x.name,
classes : [{
id : x.id,
name : x.division.name
}]
})
}
return cum;
},[]);
I use reduce and findIndex methods.
You can use array map to create a new array of objects with the new id.
const oldDatas = [
{
id: 91,
name: 'Art',
division: {
id: 4,
name: '1st level',
created_at: '2018-11-05T10:11:37+00:00',
},
created_at: '2018-11-05T10:11:37+00:00',
},
{
id: 188,
name: 'English',
division: {
id: 9,
name: '3rd level',
created_at: '2018-11-05T10:11:37+00:00',
},
created_at: '2018-11-05T10:11:37+00:00',
},
{
id: 218,
name: 'Art',
division: {
id: 11,
name: '3rd level',
created_at: '2018-11-05T10:11:37+00:00',
},
created_at: '2018-11-05T10:11:37+00:00',
},
];
const newDatas = oldDatas.map((data, index) => {
return { ...data, id: index + 1 };
});
console.log(newDatas);
my solution :
let res = initialArray.reduce((acc, obj) => {
const exist = acc.findIndex((item) => item.name === obj.name);
if (exist >= 0) {
acc[exist] = {
id: exist,
name: obj.name,
classes: [
...acc[exist].classes,
{ id: obj.id, name: obj.division.name },
],
};
} else {
acc.push({
id: acc.length,
name: obj.name,
classes: [{ id: obj.id, name: obj.division.name }],
});
}
return acc;
}, []);

Nested Array Filter/Mapping

Probably a basic question, but I've been blocked for a day now on this.
I am trying to get the correct map/filter from the following array:
[{
"id": 1,
"description": "Electric",
"subUtilityTypes": [{
"id": 5,
"description": "Grid"
},
{
"id": 6,
"description": "Solar"
}
]
},
{
"id": 2,
"description": "Gas",
"subUtilityTypes": [{
"id": 7,
"description": "Heating Oil"
},
{
"id": 8,
"description": "Natural Gas"
},
{
"id": 11,
"description": "Propane"
}
]
}
]
I want to get the id and description inside all subUtilityTypes.
This is what I've been trying:
this.options = arr1.map((parent) => {
return {
id: parent.id,
name: parent.subUtilityTypes.flatMap((child) => {
return child.description;
})
};
});
My problem with what I am trying is that instead of creating separate objects I am getting parent id and list of child names like this:
[{
"id": 1,
"name": [
"Grid",
"Solar"
]
},
{
"id": 2,
"name": [
"Heating Oil",
"Natural Gas",
"Propane"
]
}
]
Expected result should look like this:
[{
"id": 5,
"name": [
"Grid"
]
},
{
"id": 6,
"name": [
"Solar"
]
},
{
"id": 7,
"name": [
"Heating Oil"
]
}
]
Firstly use flatMap to get subUtilityTypes, then map entries:
const input = [{id:1,description:"Electric",subUtilityTypes:[{id:5,description:"Grid"},{id:6,description:"Solar"}]},{id:2,description:"Gas",subUtilityTypes:[{id:7,description:"Heating Oil"},{id:8,description:"Natural Gas"},{id:11,description:"Propane"}]}];
const res = input.flatMap(e => e.subUtilityTypes)
.map(e => ({ id: e.id, name: [e.description] }))
console.log(res)
.as-console-wrapper { max-height: 100% !important; top: 0; } /* ignore this */
Map only arr1 by returning only subUtilityTypes and then map it to get the desired result :
arr1.flatMap(item=>item.subUtilityTypes)
.map(item=>({id:item.id,name:[item.description]}))
let arr1=[
{
"id": 1,
"description": "Electric",
"subUtilityTypes": [
{
"id": 5,
"description": "Grid"
},
{
"id": 6,
"description": "Solar"
}
]
},
{
"id": 2,
"description": "Gas",
"subUtilityTypes": [
{
"id": 7,
"description": "Heating Oil"
},
{
"id": 8,
"description": "Natural Gas"
},
{
"id": 11,
"description": "Propane"
}
]
}
]
let options=arr1.flatMap(item=>item.subUtilityTypes).map(item=>({id:item.id,name:[item.description]}))
console.log(options)
You can also use reduce to achieve the same result.
arr.reduce((acc, curr) => [...acc,...curr.subUtilityTypes.map((o) => ({ id: o.id, name: [o.description] })),],[])
const arr = [
{
id: 1,
description: "Electric",
subUtilityTypes: [
{
id: 5,
description: "Grid",
},
{
id: 6,
description: "Solar",
},
],
},
{
id: 2,
description: "Gas",
subUtilityTypes: [
{
id: 7,
description: "Heating Oil",
},
{
id: 8,
description: "Natural Gas",
},
{
id: 11,
description: "Propane",
},
],
},
];
const result = arr.reduce((acc, curr) => [...acc,...curr.subUtilityTypes.map((o) => ({ id: o.id, name: [o.description] })),],[]);
console.log(result);

Reduce it array to an easier way to map

I'm developing an application and have added new items to my array: type and description.
array = [
{
"id": 1,
"description": "item1",
"date": {
"id": 1,
"name": "202001"
},
"item": {
"id": 1,
"name": "I1"
},
"type": {
"id": 1,
"name": "type1"
},
"price": 100
},
{
"id": 2,
"description": "item1",
"date": {
"id": 2,
"name": "202002"
},
"item": {
"id": 1,
"name": "I1"
},
"type": {
"id": 1,
"name": "type1"
},
"price": 200
},
{
"id": 3,
"description": "item1",
"date": {
"id": 2,
"name": "202002"
},
"item": {
"id": 2,
"name": "I2"
},
"type": {
"id": 2,
"name": "type2"
},
"price": 300
},
]
I previously did this to reduce it down to an easier way to map it:
items = array.reduce((acc, e) => {
if (!acc[e["item"]["name"]]) {
acc[e["item"]["name"]] = {
[e["date"]["name"]]: e["price"]
}
} else {
acc[e["item"]["name"]][e["date"]["name"]] = e["price"]
}
return acc
}, {})
To show the data before I did
const dates = [...new Set(Object.keys(items_dicc).map(i => Object.keys(items_dicc[i])).flat())]
{
Object.keys(items_dicc).map((item) => {
return (
<tr>
<td>{item}</td>
{dates.map((date) => <td>{items_dicc[item][date] || ''}</td>)}
</tr>
)
})
}
I need to add the description element and type.name to the above. For example for description:
description: e["description"]
To display the elements as in the table:
ITEM
DESCRIPTION
TYPE
202001
202002
I1
item1
type1
100
200
I2
item3
type2
-
300
How do I add and show?
EDIT: console.log(items_dicc[item])
{202001: 100, 202002: 200, description: "item1", type: "type1"}
202001: 100
202002: 200
description: "item1"
type: "type1"
__proto__: Object
{202002: 300, description: "item3", type: "type2"}
202002: 300
description: "item3"
type: "type2"
__proto__: Object
You can add the description and type attribute inside the reduce method like this,
array = [
{
"id": 1,
"description": "item1",
"date": {
"id": 1,
"name": "202001"
},
"item": {
"id": 1,
"name": "I1"
},
"type": {
"id": 1,
"name": "type1"
},
"price": 100
},
{
"id": 2,
"description": "item2",
"date": {
"id": 2,
"name": "202002"
},
"item": {
"id": 1,
"name": "I1"
},
"type": {
"id": 1,
"name": "type1"
},
"price": 200
},
{
"id": 3,
"description": "item3",
"date": {
"id": 2,
"name": "202002"
},
"item": {
"id": 2,
"name": "I2"
},
"type": {
"id": 2,
"name": "type2"
},
"price": 300
},
]
items = array.reduce((acc, e) => {
if (!acc[e["item"]["name"]]) {
acc[e["item"]["name"]] = {
[e["date"]["name"]]: e["price"],
'description': e['description'],
'type': e.type?.name,
}
} else {
acc[e["item"]["name"]][e["date"]["name"]] = e["price"]
}
return acc
}, {})
console.log(items);
To add the for description and name in the table,
const dates = [...new Set(Object.keys(items_dicc).map(i => Object.keys(items_dicc[i])).flat())]
{
Object.keys(items_dicc).map((item) => {
return (
<tr>
<td>{item}</td>
<td>{items_dicc[item]?.description}</td>
<td>{items_dicc[item]?.type}</td>
{dates.map((date) => <td>{items_dicc[item][date] || ''}</td>)}
</tr>
)
})
}
I have been seeing your question regarding these type of tables from yesterday. You have posted several questions with similar things. I suggest you to read some article and understand how JS array methods works instead of asking incremental questions in SO.
Asking in SO might solve your problems for now, but in the long run you will suffer as you don't seem to have a grip on how these things works.
you can simplify your solution like this.
const array = [{
"id": 1,
"description": "item1",
"date": {
"id": 1,
"name": "202001"
},
"item": {
"id": 1,
"name": "I1"
},
"type": {
"id": 1,
"name": "type1"
},
"price": 100
},
{
"id": 2,
"description": "item2",
"date": {
"id": 2,
"name": "202002"
},
"item": {
"id": 1,
"name": "I1"
},
"type": {
"id": 1,
"name": "type1"
},
"price": 200
},
{
"id": 3,
"description": "item3",
"date": {
"id": 2,
"name": "202002"
},
"item": {
"id": 2,
"name": "I2"
},
"type": {
"id": 2,
"name": "type2"
},
"price": 300
}
]
const result = array.map(item => {
return Object.keys(item).reduce((a, c) => {
if (c === "date") {
a[item[c].name] = item.price;
} else if (c !== "price" && c !== "id") {
a[c] = (typeof item[c] === "object") ? item[c].name : item[c];
}
return a;
}, {})
});
console.log(result);

Only show list of items that match all values in different array

I currently have an array of selectedCategories and list of products that looks like this:
let selectedCategories = []
let productList = []
and a JSON list of products which looks like this:
let products = [
{
"id": 1,
...
"categories": [
{ "name": "Category 1", "count": 1 },
{ "name": "Category 2", "count": 1 },
{ "name": "Category 3", "count": 1 }
],
...
},
{
"id": 2,
...
"categories": [
{ "name": "Category 4", "count": 1 },
{ "name": "Category 5", "count": 2 },
{ "name": "Category 6", "count": 1 }
],
...
},
{
"id": 3,
...
"categories": [
{ "name": "Category 5", "count": 1 }
],
...
}
]
I only want to show the products that match the values in the selectedCategories array. I've tried both every and includes, but cannot seem to get the desired result:
this.products.filter(product => {
product.categories.filter(category => {
if (this.selectedCategories.includes(category.name)) {
productList.push(product)
}
})
})
and
this.products.filter(product => {
product.categories.filter(category => {
this.selectedCategories.every(value => {
if (value === category.name) {
productList.push(product)
}
})
})
})
The problem is that in the code above, it doesnt check if all values in the array match, but only if it occurs.
Wished output is something like:
selectedCategories = [ "Category 5" ]
>>> productList = [{id: 2}, {id: 3}]
selectedCategories = [ "Category 5", "Category 4" ]
>>> productList = [{id: 2}]
How can i achieve this?
Edit:
How can i filter on multiple arrays in the product object?
Product structure:
[
{
"id": 1,
...
"categories": [
{ "name": "Category 1", "count": 1 },
{ "name": "Category 2", "count": 1 },
{ "name": "Category 3", "count": 1 }
],
"styles": [
{ name: "Style 1", count: 1 },
{ name: "Style 2", count: 1 },
]
...
},
{
"id": 2,
...
"categories": [
{ "name": "Category 4", "count": 1 },
{ "name": "Category 5", "count": 2 },
{ "name": "Category 6", "count": 1 }
],
"styles": [
{ name: "Style 3", count: 1 },
{ name: "Style 4", count: 1 },
]
...
},
{
"id": 3,
...
"categories": [
{ "name": "Category 5", "count": 1 }
],
"styles": [
{ name: "Style 3", count: 1 },
]
...
}
]
Expected output:
selectedCategories = [ "Category 5" ]
>>> productList = [{id: 2}, {id: 3}]
selectedCategories = [ "Category 5", "Style 3" ]
>>> productList = [{id: 2}, {id: 3}]
selectedCategories = [ "Category 5", "Style 3", "Style 4" ]
>>> productList = [{id: 2}]
You need filter+every+some+map to get your desired output:
const products = [ { "id": 1, "categories": [ { "name": "Category 1", "count": 1 }, { "name": "Category 2", "count": 1 }, { "name": "Category 3", "count": 1 } ], "styles": [ { name: "Style 1", count: 1 }, { name: "Style 2", count: 1 }, ] }, { "id": 2, "categories": [ { "name": "Category 4", "count": 1 }, { "name": "Category 5", "count": 2 }, { "name": "Category 6", "count": 1 } ], "styles": [ { name: "Style 3", count: 1 }, { name: "Style 4", count: 1 }, ] }, { "id": 3, "categories": [ { "name": "Category 5", "count": 1 } ], "styles": [ { name: "Style 3", count: 1 }, ] } ];
const selectedCategories = [ "Category 5", "Style 3", "Style 4" ]
const result = products.filter(p=>selectedCategories.every(k=>p.categories.some(t=>t.name==k) || p.styles.some(s=>s.name==k))).map(({id})=>({id}));
console.log(result);
You could combine the use of filter and every
const getProductList = (products, selectedCategories) => {
return products
.filter((product) => {
const productCategories = product.categories.map(
(category) => category.name
)
return selectedCategories.every((selectedCategory) =>
productCategories.includes(selectedCategory)
)
})
.map((product) => ({ id: product.id }))
}
Full demo
const products = [
{
id: 1,
categories: [
{ name: "Category 1", count: 1 },
{ name: "Category 2", count: 1 },
{ name: "Category 3", count: 1 },
],
},
{
id: 2,
categories: [
{ name: "Category 4", count: 1 },
{ name: "Category 5", count: 2 },
{ name: "Category 6", count: 1 },
],
},
{
id: 3,
categories: [{ name: "Category 5", count: 1 }],
},
]
const getProductList = (products, selectedCategories) => {
return products
.filter((product) => {
const productCategories = product.categories.map(
(category) => category.name
)
return selectedCategories.every((selectedCategory) =>
productCategories.includes(selectedCategory)
)
})
.map((product) => ({ id: product.id }))
}
console.log(getProductList(products, ["Category 5"]))
console.log(getProductList(products, ["Category 5", "Category 4"]))
.as-console-wrapper { max-height: 100% !important; }
You should return (either with the return keyword or with the implicit return of arrow functions) inside the array methods for them to be useful. After filtering, .map each to an object containing only the ID:
const getFiltered = (selectedCategories) => {
const productList = products
.filter(({ categories }) => selectedCategories.every(
requiredCat => categories.some(
prodCat => prodCat.name === requiredCat
)
))
.map(({ id }) => ({ id }));
console.log(productList);
};
const products = [
{
"id": 1,
"categories": [
{ "name": "Category 1", "count": 1 },
{ "name": "Category 2", "count": 1 },
{ "name": "Category 3", "count": 1 }
],
},
{
"id": 2,
"categories": [
{ "name": "Category 4", "count": 1 },
{ "name": "Category 5", "count": 2 },
{ "name": "Category 6", "count": 1 }
],
},
{
"id": 3,
"categories": [
{ "name": "Category 5", "count": 1 }
],
}
];
getFiltered(['Category 5', 'Category 4']);
getFiltered(['Category 5']);
You need to iterate the cateories as well.
const
filterByCategories = (products, categories) =>
products.filter(product =>
categories.every(category =>
product.categories.some(c => category === c.name)
)
),
products = [{ id: 1, categories: [{ name: "Category 1", count: 1 }, { name: "Category 2", count: 1 }, { name: "Category 3", count: 1 }] }, { id: 2, categories: [{ name: "Category 4", count: 1 }, { name: "Category 5", count: 2 }, { name: "Category 6", count: 1 }] }, { id: 3, categories: [{ name: "Category 5", count: 1 }] }];
console.log(filterByCategories(products, ["Category 5"])); [{id: 2}, {id: 3}]
console.log(filterByCategories(products, ["Category 5", "Category 4"])); [{id: 2}]

Categories