How to filter nested arrays by searching - javascript

I have an array of objects that I want to filter by comparing a nested property to a search term.
For example:
let array = [
{
category: 15,
label: "Components",
value: "a614741f-7d4b-4b33-91b7-89a0ef96a099",
children: [
{
category: 1,
label: "Carousel1",
diId: 55946,
// as you can see there are many children nested array of object
children: [{ label: "nodatafoundmessage", value: "47d18fb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "be5e027b-9163-4cfb-8816-0c8e3b816086"
},
{
category: 2,
label: "Checkbox1",
diId: 193909,
children: [{ label: "datafound", value: "47d18sb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "045e8786-2165-4e1e-a839-99b1b0ceef57"
}
]
},
{
value: "4be22726-850c-4905-ab3b-039fcf607d55",
label: "Default",
children: [
{
category: 5,
defaultValueType: 1,
label: "Empty",
toType: "String",
value: "ebedb43f-4c53-491f-8954-d030321845cd"
},
{
category: 5,
defaultValueType: 2,
label: "Space",
toType: "String",
value: "2d0e1429-572b-4f21-9f83-3340bafff95a"
},
{
category: 5,
defaultValueType: 8,
label: "Current Username",
toType: "String",
value: "25f6b40a-33c7-4f17-b29d-99e8d1e4e33c"
},
{
category: 5,
defaultValueType: 9,
label: "Current Location",
toType: "Location",
value: "ed59da2f-318d-4599-9085-4d9d769a27d7"
}
]
},
{
category: 4,
label: "Fixed Value",
isFixed: true,
value: "28e90e3e-a20b-4499-9593-061a7d1e7bd6"
// as you can see there is no children in this object
}
]};
What I'm trying to achieve is if I search for 'nodata' for example my result should be
let array = [
{
category: 15,
label: "Components",
value: "a614741f-7d4b-4b33-91b7-89a0ef96a099",
children: [
{
category: 1,
label: "Carousel1",
diId: 55946,
// as you can see there are many children nested array of object
children: [{ label: "nodatafoundmessage", value: "47d18fb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "be5e027b-9163-4cfb-8816-0c8e3b816086"
}
]
}
];
Another option if I search for 'spa' my result should be
let array = [
{
value: "4be22726-850c-4905-ab3b-039fcf607d55",
label: "Default",
children: [
{
category: 5,
defaultValueType: 2,
label: "Space",
toType: "String",
value: "2d0e1429-572b-4f21-9f83-3340bafff95a"
}
]
}
];
I have been super confused and I decided to get some help. Thank you for your helps guys!

The following function should do the trick for you:
function searchData(dataArray, searchTerm) {
return dataArray.flatMap(obj => {
const objHasSearchTerm = Object.entries(obj)
.some(([key, value]) => key !== 'children' && String(value).toLowerCase().includes(searchTerm.toLowerCase()));
if (objHasSearchTerm && !obj.children) return [obj];
const matchedChildren = searchData(obj.children ?? [], searchTerm);
return objHasSearchTerm || matchedChildren.length > 0
? [{
...obj,
children: matchedChildren,
}]
: [];
})
}
It recursively goes through the data array, looks for any entries that have the specified search term, and if so, places it into the newly constructed object. It will preserve the nested shape of the object, which may or may not be what is needed. Feel free to tweak the algorithm to your own needs.
let allData = [
{
category: 15,
label: "Components",
value: "a614741f-7d4b-4b33-91b7-89a0ef96a099",
children: [
{
category: 1,
label: "Carousel1",
diId: 55946,
// as you can see there are many children nested array of object
children: [{ label: "nodatafoundmessage", value: "47d18fb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "be5e027b-9163-4cfb-8816-0c8e3b816086"
},
{
category: 2,
label: "Checkbox1",
diId: 193909,
children: [{ label: "datafound", value: "47d18sb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "045e8786-2165-4e1e-a839-99b1b0ceef57"
}
]
},
{
value: "4be22726-850c-4905-ab3b-039fcf607d55",
label: "Default",
children: [
{
category: 5,
defaultValueType: 1,
label: "Empty",
toType: "String",
value: "ebedb43f-4c53-491f-8954-d030321845cd"
},
{
category: 5,
defaultValueType: 2,
label: "Space",
toType: "String",
value: "2d0e1429-572b-4f21-9f83-3340bafff95a"
},
{
category: 5,
defaultValueType: 8,
label: "Current Username",
toType: "String",
value: "25f6b40a-33c7-4f17-b29d-99e8d1e4e33c"
},
{
category: 5,
defaultValueType: 9,
label: "Current Location",
toType: "Location",
value: "ed59da2f-318d-4599-9085-4d9d769a27d7"
}
]
},
{
category: 4,
label: "Fixed Value",
isFixed: true,
value: "28e90e3e-a20b-4499-9593-061a7d1e7bd6"
// as you can see there is no children in this object
}
];
function searchData(dataArray, searchTerm) {
return dataArray.flatMap(obj => {
const objHasSearchTerm = Object.entries(obj)
.some(([key, value]) => key !== 'children' && String(value).toLowerCase().includes(searchTerm.toLowerCase()));
if (objHasSearchTerm && !obj.children) return [obj];
const matchedChildren = searchData(obj.children ?? [], searchTerm);
return objHasSearchTerm || matchedChildren.length > 0
? [{
...obj,
children: matchedChildren,
}]
: [];
})
}
console.log('----- Search: nodata')
console.log(JSON.stringify(searchData(allData, 'nodata'), null, 2))
console.log('----- Search: spa')
console.log(JSON.stringify(searchData(allData, 'spa'), null, 2))

Related

How to remove ids from array

I am trying through the array and use a Array.prototype.filter() method on every children array to find the elements whose key matches with the ones specified.
Then, I'am using Array.prototype.splice() to remove the results from the respective children array but the result is return undefined.
const inputArray = [
"Oxygen-a3b8be32-c36e-a02e-37f4-a35239e0cedb",
"633ac872e78fa7ebee03b8bf",
"5e69dbd7-5fee-67a9-c73f-4656f9b90715",
"d484558b-4717-b0b8-db07-68288afb4f6a",
"63922aac4ff08f52d71fa891",
"33a3182b-93a4-84b9-4c49-c955a8416197",
];
const originalArray = [{
title: "Animals",
key: "d484558b-4717-b0b8-db07-68288afb4f6a",
children: [{
title: "Color",
key: "63922aac4ff08f52d71fa891",
children: [{
title: "Black",
key: "Black-9e994ed2-823b-d1d6-4613-91d43f570fec",
},
{
title: "White",
key: "White-5d0b102a-2555-8f7c-d471-cc82a5bd9c01",
},
],
}, ],
},
{
title: "Elements",
key: "5e69dbd7-5fee-67a9-c73f-4656f9b90715",
children: [{
title: "Non metals",
key: "633ac872e78fa7ebee03b8bf",
children: [{
title: "Carbon",
key: "Carbon-e443daa4-def4-9830-796e-ee8c5a1f41d4",
},
{
title: "Nitrogen",
key: "Nitrogen-c2922569-0b2d-0e07-454d-d8411af701b7",
},
{
title: "Oxygen",
key: "Oxygen-a3b8be32-c36e-a02e-37f4-a35239e0cedb",
},
],
}, ],
},
{
title: "Planets",
key: "33a3182b-93a4-84b9-4c49-c955a8416197",
children: [{
title: "Composition",
key: "63b3d5cd12c06ba7ce353f76",
children: [{
title: "Chthonian planet",
key: "Chthonian planet-b3c593c1-d29e-5e14-1b11-2241e8ef2be6",
},
{
title: "Carbon planet",
key: "Carbon planet-07d67d62-afcf-fbcf-a8e8-75081cb44c2f",
},
],
}, ],
},
];
console.log(
"🚀 ~ file: TranferTree.misc.js:152 ~ onCheck ~ outputArray",
originalArray.forEach(e => {
e.children.forEach((c, i) => {
if (inputArray.includes(c.key)) {
e.children.splice(i, 1);
} else {
c.children.forEach((cc, j) => {
if (inputArray.includes(cc.key)) {
c.children.splice(j, 1);
}
});
}
});
})
);
Note: For example in the Elements => 5e69dbd7-5fee-67a9-c73f-4656f9b90715 children Non metals => 633ac872e78fa7ebee03b8bf i am only remove object with this key => Oxygen-a3b8be32-c36e-a02e-37f4-a35239e0cedb I want to keep the other objects that were not found this also applies to for example Composition => 63b3d5cd12c06ba7ce353f76 or Planets => 33a3182b-93a4-84b9-4c49-c955a8416197.
Since you want to preserve the original object references it will be slightly less efficient, but here's a way you can do it with recursive function calls. It provides the same output as your code, but it's correctly logging the final structure whereas yours is logging the return value of .forEach() which is undefined, by design.
const inputArray = [
"Oxygen-a3b8be32-c36e-a02e-37f4-a35239e0cedb",
"633ac872e78fa7ebee03b8bf",
"5e69dbd7-5fee-67a9-c73f-4656f9b90715",
"d484558b-4717-b0b8-db07-68288afb4f6a",
"63922aac4ff08f52d71fa891",
"33a3182b-93a4-84b9-4c49-c955a8416197",
];
const originalArray = [{
title: "Animals",
key: "d484558b-4717-b0b8-db07-68288afb4f6a",
children: [{
title: "Color",
key: "63922aac4ff08f52d71fa891",
children: [{
title: "Black",
key: "Black-9e994ed2-823b-d1d6-4613-91d43f570fec",
},
{
title: "White",
key: "White-5d0b102a-2555-8f7c-d471-cc82a5bd9c01",
},
],
}, ],
},
{
title: "Elements",
key: "5e69dbd7-5fee-67a9-c73f-4656f9b90715",
children: [{
title: "Non metals",
key: "633ac872e78fa7ebee03b8bf",
children: [{
title: "Carbon",
key: "Carbon-e443daa4-def4-9830-796e-ee8c5a1f41d4",
},
{
title: "Nitrogen",
key: "Nitrogen-c2922569-0b2d-0e07-454d-d8411af701b7",
},
{
title: "Oxygen",
key: "Oxygen-a3b8be32-c36e-a02e-37f4-a35239e0cedb",
},
],
}, ],
},
{
title: "Planets",
key: "33a3182b-93a4-84b9-4c49-c955a8416197",
children: [{
title: "Composition",
key: "63b3d5cd12c06ba7ce353f76",
children: [{
title: "Chthonian planet",
key: "Chthonian planet-b3c593c1-d29e-5e14-1b11-2241e8ef2be6",
},
{
title: "Carbon planet",
key: "Carbon planet-07d67d62-afcf-fbcf-a8e8-75081cb44c2f",
},
],
}, ],
},
];
function filterChildrenById (item, ids) {
if (item.children) {
for (let i = 0; i < item.children.length; i++) {
let child = item.children[i];
if (ids.includes(child.key)) {
item.children.splice(i, 1);
// Reduce index because we removed an item so indexing will
// be off if we don't do this
i--;
} else if (Array.isArray(child.children)) {
child = filterChildrenById(child, ids);
}
}
}
return item;
}
function filterData(data, ids) {
data.forEach(item => filterChildrenById(item, ids))
return data;
}
console.log(
"🚀 ~ file: TranferTree.misc.js:152 ~ onCheck ~ outputArray",
filterData(originalArray, inputArray)
);
You need to iterate from the end of the array, because splice changes index for the followind item.
const
keys = ["Oxygen-a3b8be32-c36e-a02e-37f4-a35239e0cedb", "633ac872e78fa7ebee03b8bf", "5e69dbd7-5fee-67a9-c73f-4656f9b90715", "d484558b-4717-b0b8-db07-68288afb4f6a", "63922aac4ff08f52d71fa891", "33a3182b-93a4-84b9-4c49-c955a8416197"],
data = [{ title: "Animals", key: "d484558b-4717-b0b8-db07-68288afb4f6a", children: [{ title: "Color", key: "63922aac4ff08f52d71fa891", children: [{ title: "Black", key: "Black-9e994ed2-823b-d1d6-4613-91d43f570fec" }, { title: "White", key: "White-5d0b102a-2555-8f7c-d471-cc82a5bd9c01" }] }] }, { title: "Elements", key: "5e69dbd7-5fee-67a9-c73f-4656f9b90715", children: [{ title: "Non metals", key: "633ac872e78fa7ebee03b8bf", children: [{ title: "Carbon", key: "Carbon-e443daa4-def4-9830-796e-ee8c5a1f41d4" }, { title: "Nitrogen", key: "Nitrogen-c2922569-0b2d-0e07-454d-d8411af701b7" }, { title: "Oxygen", key: "Oxygen-a3b8be32-c36e-a02e-37f4-a35239e0cedb" }] }] }, { title: "Planets", key: "33a3182b-93a4-84b9-4c49-c955a8416197", children: [{ title: "Composition", key: "63b3d5cd12c06ba7ce353f76", children: [{ title: "Chthonian planet", key: "Chthonian planet-b3c593c1-d29e-5e14-1b11-2241e8ef2be6" }, { title: "Carbon planet", key: "Carbon planet-07d67d62-afcf-fbcf-a8e8-75081cb44c2f" }] }] }],
remove = keys => {
const fn = array => {
let i = array.length;
while (i--) {
if (keys.includes(array[i].key)) array.splice(i, 1);
else if (array[i].children) fn(array[i].children);
}
};
return fn;
};
remove(keys)(data);
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

i wanna get the max depth of a not-certain tree. the tree looks like below,how can i finish it

given a tree-structured data, get the max height of the tree. i wanna get the max depth of a not-certain tree. the tree looks like below:
{
id: 1,
label: 'label1',
children: [{
id: 3,
label: 'label2',
children: [{
id: 4,
label: 'label3'
}, {
id: 5,
label: 'label4',
disabled: true,
children: [{
id: 4,
label: 'label3'
}, {
id: 5,
label: 'label4',
disabled: true
}]
}]
}
i tried as below, but it did not work as expected.
const maxDepth = o => {
if(!o || !o.children) return 0;
let arr = []
for(let i = 0; i< o.children.length; i++) {
arr[i] = maxDepth(o.children[i])
}
let max = Math.max(...[arr]) + 1
return max
}
I don't believe your data is formatted 100% correctly, so I took the liberty of doing so. That being said, this screams for a recursive algorithm.
{
id: 1,
label: 'label1',
children: [{
id: 3,
label: 'label2',
children: [{
id: 4,
label: 'label3'
},
{
id: 5,
label: 'label4',
disabled: true,
children: [{
id: 4,
label: 'label3'
},
{
id: 5,
label: 'label4',
disabled: true
}]
}]
}]
}
test1 = {
id: 1,
label: "test1",
children: []
}
test2 = {
id: 2,
label: "test1",
children: [
{
id: 2,
label: "test2",
children: []
},
{
id: 2,
label: "test2",
children: []
}]
}
test3 = {
id: 3,
label: "test1",
children: [
{
id: 3,
label: "test2",
children: [{
children: [{
children: [{}]
}]
}]
},
{
id: 3,
label: "test2",
children: [{}]
}]
}
your_data = {
id: 1,
label: 'label1',
children: [{
id: 3,
label: 'label2',
children: [{
id: 4,
label: 'label3'
},
{
id: 5,
label: 'label4',
disabled: true,
children: [{
id: 4,
label: 'label3'
},
{
id: 5,
label: 'label4',
disabled: true
}]
}]
}]
}
my_data = {
id: 1,
label: 'label1',
children: [{
id: 3,
label: 'label2',
children: [{
id: 4,
label: 'label3'
},
{
id: 5,
label: 'label4',
disabled: true,
children: [{
id: 4,
label: 'label3'
},
{
id: 5,
label: 'label4',
disabled: true
}]
}]
},
{
id: 6,
label: 'madeup1',
children: [{
id: 7,
label: 'madeup2',
children: [{
id: 8,
label: 'madeup3',
children: [{
id: 9,
label: 'madeup4'
}]
}]
}]
}]
}
function max_depth(exploringTheDepthsOf)
{
largest = 0;
if (exploringTheDepthsOf.hasOwnProperty('children'))
{
for (var i = 0; i < exploringTheDepthsOf["children"].length; i++)
{
largest = Math.max(largest, max_depth(exploringTheDepthsOf["children"][i]));
}
}
else
{
return 0;
}
return largest + 1;
}
console.log("returned value", max_depth(test1));
console.log("returned value", max_depth(test2));
console.log("returned value", max_depth(test3));
console.log("returned value", max_depth(your_data));
console.log("returned value", max_depth(my_data));
This is about as close as I could get. Geeks for geeks has a pretty good artical on it and javascript code to show you how to do it, but its for actual nodes, not for json like objects: https://www.geeksforgeeks.org/depth-n-ary-tree/

Filter Array of objects with nested array

so I am trying to set up a nested filter on an array of objects.
The thing is that the filter is applied inside the object on a key that is another array of objects.
here is the code:
const items = [
{ name: "123", id: 1, value: true, arr: [{ id: 1 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 2 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 3 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 4 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 5 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 6 }] },
];
const newArray = items.filter((objects) => {
objects.arr.filter((item) => {
if (item.id === 2) {
return objects;
}
});
});
console.log(newArray);
I 'm not sure where to put the return because in this situation i just get an empty array.
You need to check the nested array contains the wanted id and return the result to the filter method.
const
items = [{ name: "123", id: 1, value: true, arr: [{ id: 1 }] }, { name: "456", id: 2, value: false, arr: [{ id: 2 }] }, { name: "456", id: 2, value: false, arr: [{ id: 3 }] }, { name: "456", id: 2, value: false, arr: [{ id: 4 }] }, { name: "456", id: 2, value: false, arr: [{ id: 5 }] }, { name: "456", id: 2, value: false, arr: [{ id: 6 }] }],
result = items.filter(({ arr }) => arr.some(({ id }) => id === 2));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Use Array#some to check if current arr has an element with id equal to 2:
const items = [ { name: "123", id: 1, value: true, arr: [{ id: 1 }] }, { name: "456", id: 2, value: false, arr: [{ id: 2 }] }, { name: "456", id: 2, value: false, arr: [{ id: 3 }] }, { name: "456", id: 2, value: false, arr: [{ id: 4 }] }, { name: "456", id: 2, value: false, arr: [{ id: 5 }] }, { name: "456", id: 2, value: false, arr: [{ id: 6 }] } ];
const newArray = items.filter(({ arr = [] }) =>
arr.some(({ id }) => id === 2)
);
console.log(newArray);
As you contains only one object in arr then you can access this object by using [0] index.
Working Demo :
const items = [
{ name: "123", id: 1, value: true, arr: [{ id: 1 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 2 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 3 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 4 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 5 }] },
{ name: "456", id: 2, value: false, arr: [{ id: 6 }] },
];
const newArray = items.filter((obj) => {
if (obj.arr[0].id === 2) {
return obj;
}
});
console.log(newArray);
I answered you as per the issue you are facing but if you have multiple objects in your arr then you can go ahead with Array.some() method as suggested by other answers.

Filter object from array based on multiple nested values

Basically I'm trying to figure out the cleanest way to select one item from an array, only if all certain values exist.
const filterValues = ['blue', '30cm', 'true'];
const products = [
{
details: [
{ id: 1, value: 'red' },
{ id: 2, value: '30cm' },
{ id: 3, value: 'true' },
{ id: 4, value: '123432'}
],
name: "Product 1"
},
{
details: [
{ id: 5, value: 'blue' },
{ id: 6, value: '30cm' },
{ id: 7, value: 'true' },
{ id: 8, value: '98348'}
],
name: "Product 2"
},
{
details: [
{ id: 9, value: 'black' },
{ id: 10, value: '40cm' },
{ id: 11, value: 'false' },
{ id: 12, value: '578347'}
],
name: "Product 3"
},
]
Only Product 2 contains all the filter values, so I want to return that product.
I have tried:
products.filter(p => {
p.details.find(k => filterValues.includes(k.value));
})
but this returns if any of the values satisfies the condition rather than if all of them are included. This is the main issue here. I'm struggling with finding a way to filter if only all these values int he array are present in the object.
Use Array#every.
const filterValues = ['blue', '30cm', 'true'];
const products = [
{
details: [
{ id: 1, value: 'red' },
{ id: 2, value: '30cm' },
{ id: 3, value: 'true' },
{ id: 4, value: '123432'}
],
name: "Product 1"
},
{
details: [
{ id: 5, value: 'blue' },
{ id: 6, value: '30cm' },
{ id: 7, value: 'true' },
{ id: 8, value: '98348'}
],
name: "Product 2"
},
{
details: [
{ id: 9, value: 'black' },
{ id: 10, value: '40cm' },
{ id: 11, value: 'false' },
{ id: 12, value: '578347'}
],
name: "Product 3"
},
]
console.log(products.filter(p => filterValues.every(fv => p.details.map(d => d.value).includes(fv))));

Cartesian array based on array of objects in Javascript

I'm looking for method to find cartesian array based on array of objects.
Basicly I've seen solutions like that:
Cartesian product of multiple arrays in JavaScript
but I'm not sure how to modify it to work on object property (in my case on property "value").
For instance my input:
let arr1 = [
{
id: 1,
type: "attribute",
value: "arr1-attr1"
},
{
id: 2,
type: "attribute",
value: "arr1-attr2"
}
];
let arr2 = [
{
id: 3,
type: "attribute",
value: "arr2-attr1"
}
];
let arr3 = [
{
id: 4,
type: "attribute",
value: "arr3-attr1"
},
{
id: 5,
type: "attribute",
value: "arr3-attr2"
}
];
Expected output:
output = [
[
{
id: 1,
type: "attribute",
value: "arr1-attr1"
},
{
id: 3,
type: "attribute",
value: "arr2-attr1"
},
{
id: 4,
type: "attribute",
value: "arr3-attr1"
}
],
[
{
id: 2,
type: "attribute",
value: "arr1-attr2"
},
{
id: 3,
type: "attribute",
value: "arr2-attr1"
},
{
id: 5,
type: "attribute",
value: "arr3-attr2"
}
],
[
{
id: 1,
type: "attribute",
value: "arr1-attr1"
},
{
id: 3,
type: "attribute",
value: "arr2-attr1"
},
{
id: 4,
type: "attribute",
value: "arr3-attr1"
}
],
[
{
id: 2,
type: "attribute",
value: "arr1-attr2"
},
{
id: 3,
type: "attribute",
value: "arr2-attr1"
},
{
id: 5,
type: "attribute",
value: "arr3-attr2"
}
]
];
Just take the single arrays as items of a collection of this arrays and perform the algorithm on this data set.
var arr1 = [{ id: 1, type: "attribute", value: "arr1-attr1" }, { id: 2, type: "attribute", value: "arr1-attr2" }],
arr2 = [{ id: 3, type: "attribute", value: "arr2-attr1" }],
arr3 = [{ id: 4, type: "attribute", value: "arr3-attr1" }, { id: 5, type: "attribute", value: "arr3-attr2" }],
result = [arr1, arr2, arr3]
.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories