I am trying to filter a multi-nested complex array of objects on a specific value. For example, I want to filter a car models array according to the incoming parameter, say "bmw". The expected output should be an array of top level objects which has the incoming parameter (i.e. "bmw" here) as a value in any position of the complex object.
I have tried several approaches without success and I have posted two attempts below together with the output received.
Is there something wrong with my approach or is it my code?
const arr = [
{
id:"1",
name:"name1",
assets: [
{
cars: {
brand : [
{
model: "bmw"
},
{
model: "dodge"
}
]
}
}
],
assets: [
{
cars: {
brand : [
{
model: "ford"
},
{
model: "ferrari"
}
]
}
}
],
},{
id:"2",
name:"name2",
assets: [
{
cars: {
brand : [
{
model: "audi"
},
{
model: "maseratti"
}
]
}
}
],
assets: [
{
cars: {
brand : [
{
model: "mercedes"
},
{
model: "bmw"
}
]
}
}
],
}
]
If we use, for example:
let search="bmw"
then the expected output would be the following array of objects:
[
{
id:"1",
name:"name1",
assets: [
{
cars: {
brand : [
{
model: "bmw"
},
{
model: "dodge"
}
]
}
}
],
},
{
id:"2",
name:"name2",
assets: [
{
cars: {
brand : [
{
model: "mercedes"
},
{
model: "bmw"
}
]
}
}
],
}
]
Attempt 1:
const arr = [{ id: "1", name: "name1", assets: [{ cars: { brand: [{ model: "bmw" }, { model: "dodge" } ] } }], assets: [{ cars: { brand: [{ model: "ford" }, { model: "ferrari" } ] } }],}, { id: "2", name: "name2", assets: [{ cars: { brand: [{ model: "audi" }, { model: "maseratti" } ] } }], assets: [{ cars: { brand: [{ model: "mercedes" }, { model: "bmw" } ] } }],}];
let search="bmw"
let filtered= [];
arr.filter((person)=>{
person.assets.filter((car) => {
car.cars.brand.filter((m) => {
if(m.model.toLowerCase().includes(search.toLowerCase())) {
filtered.push(m)
}
})
})
});
console.log(arr);
/*********** console output:
[{
assets: [{
cars: {
brand: [{
model: "ford"
}, {
model: "ferrari"
}]
}
}],
id: "1",
name: "name1"
}, {
assets: [{
cars: {
brand: [{
model: "mercedes"
}, {
model: "bmw"
}]
}
}],
id: "2",
name: "name2"
}]
***********/
Attempt 2:
const arr = [{ id: "1", name: "name1", assets: [{ cars: { brand: [{ model: "bmw" }, { model: "dodge" } ] } }], assets: [{ cars: { brand: [{ model: "ford" }, { model: "ferrari" } ] } }],}, { id: "2", name: "name2", assets: [{ cars: { brand: [{ model: "audi" }, { model: "maseratti" } ] } }], assets: [{ cars: { brand: [{ model: "mercedes" }, { model: "bmw" } ] } }],}];
let search="bmw"
const q = arr.filter((element) => {
element.assets.filter((element1) => {
element1.cars.brand.filter((m) =>
m.model.toLowerCase().includes(search.toLowerCase()))
.map(element=> {
return Object.assign({}, element, {element1});
})
})
})
console.log(q);
/*********** console output:
[]
************/
CodePudding user response:
Having corrected the initially given arr
structure, a solution is to drill into the structure with an outer call to .filter
and inner calls to .some
:
const arr = [
{
id:"1",
name:"name1",
assets: [
{
cars: {
brand : [
{ model: "bmw" },
{ model: "dodge" },
{ model: "ford" },
{ model: "ferrari" }
]
}
}
],
},
{
id:"2",
name:"name2",
assets: [
{
cars: {
brand : [
{ model: "audi" },
{ model: "maseratti" },
{ model: "mercedes" }
]
}
}
],
}
];
function filterUsersByCar(carBrand, users) {
return users.filter((user) => {
const carAssets = user.assets.find((asset) => 'cars' in asset);
return carAssets && carAssets.cars.brand.some(({ model }) => {
return model === carBrand;
});
});
}
console.log(filterUsersByCar('bmw', arr));
Some things to note:
The inner carAssets
is needed because the given arr
structure implies that it might be possible that a user has something different than car models inside assets
. This would mean that a script first has to find out if any of the items in assets
deals with cars. The shown solution uses the logik "if any of the assets
items has a field 'cars', the whole item is about cars". This leads to "if there is no 'cars' asset, the whole user should be excluded from the result set". I hope that is right.
If the user can only have assets that are cars, it might be possible to simplify the user structure (read: "item in arr
") such that the assets
property directly includes the objects which are currently placed inside assets.cars.brand
. That would also remove the need for the hasCarAssets
check. If that is an option, it would change the solution to:
const arr = [
{
id:"1",
name:"name1",
assets: [
{ model: "bmw" },
{ model: "dodge" },
{ model: "ford" },
{ model: "ferrari" }
],
},
{
id:"2",
name:"name2",
assets: [
{ model: "audi" },
{ model: "maseratti" },
{ model: "mercedes" }
],
}
];
function filterUsersByCar(carBrand, users) {
return users.filter((user) => {
return user.assets.some(({ model }) => model === carBrand);
});
}
console.log(filterUsersByCar('bmw', arr));