Hitting a wall with this one, thought I would post it here in case some kind soul has come across a similar one. I have some data that looks something like this:
<
I think it will be a recursive solution. Here is one that I tried.
function find(obj, key) {
if (obj.value && obj.value.indexOf(key) > -1){
return true;
}
if (obj.children && obj.children.length > 0){
return obj.children.reduce(function(obj1, obj2){
return find(obj1, key) || find(obj2, key);
}, {});
}
return false;
}
var output = input.filter(function(obj){
return find(obj, 'Hit');
});
console.log('Result', output);
Here is a good solution which utilizes the array reduce
function which results in more readable code then the other solutions. Also it is more readable in my opinion. We are calling the filter
function recursively to filter an array along with its children
const input = [
{
value: "Miss1",
children: [
{ value: "Miss2" },
{ value: "Hit1", children: [{ value: "Miss3" }] },
],
},
{
value: "Miss4",
children: [
{ value: "Miss5" },
{ value: "Miss6", children: [{ value: "Hit2" }] },
],
},
{
value: "Miss7",
children: [
{ value: "Miss8" },
{ value: "Miss9", children: [{ value: "Miss10" }] },
],
},
{
value: "Hit3",
children: [
{ value: "Miss11" },
{ value: "Miss12", children: [{ value: "Miss13" }] },
],
},
{
value: "Miss14",
children: [
{ value: "Hit4" },
{ value: "Miss15", children: [{ value: "Miss16" }] },
],
},
];
function recursiveFilter(arr) {
return arr.reduce(function filter(prev, item) {
if (item.value.includes("Hit")) {
if (item.children && item.children.length > 0) {
return prev.concat({
...item,
children: item.children.reduce(filter, []),
});
} else {
return item;
}
}
return prev;
}, []);
}
console.log(recursiveFilter(input));
Alternatively you can use _.filterDeep
method from deepdash extension for lodash
:
var keyword = 'Hit';
var foundHit = _.filterDeep(
input,
function(value) {
return value.value.includes(keyword);
},
{
tree: true,
onTrue: { skipChildren: true },
}
);
Here is a full test for your case
Array.prototype.flatMap
is a good fit for recursive filtering. Similar to map
, filter
and reduce
, using flatMap
does not modify the original input -
const findHits = (t = []) =>
t.flatMap(({ value, children }) => {
if (value.startsWith("Hit"))
return [{ value, children }]
else {
const r = findHits(children)
return r.length ? [{ value, children: r }] : []
}
})
const input =
[{value:'Miss1',children:[{value:'Miss2'},{value:'Hit1', children:[{value:'Miss3'}]}]},{value:'Miss4',children:[{value:'Miss5'},{value:'Miss6', children:[{value:'Hit2'}]}]},{value:'Miss7',children:[{value:'Miss8'},{value:'Miss9', children:[{value:'Miss10'}]}]},{value:'Hit3',children:[{value:'Miss11'},{value:'Miss12', children:[{value:'Miss13'}]}]},{value:'Miss14',children:[{value:'Hit4'},{value:'Miss15', children:[{value:'Miss16'}]}]}]
const result =
findHits(input)
console.log(JSON.stringify(result, null, 2))
[
{
"value": "Miss1",
"children": [
{
"value": "Hit1",
"children": [
{
"value": "Miss3"
}
]
}
]
},
{
"value": "Miss4",
"children": [
{
"value": "Miss6",
"children": [
{
"value": "Hit2"
}
]
}
]
},
{
"value": "Hit3",
"children": [
{
"value": "Miss11"
},
{
"value": "Miss12",
"children": [
{
"value": "Miss13"
}
]
}
]
},
{
"value": "Miss14",
"children": [
{
"value": "Hit4"
}
]
}
]
const input = [
{
value: 'Miss1',
children: [
{ value: 'Miss1' },
{ value: 'Hit1', children: [ { value: 'Miss3' } ] }
]
},
{
value: 'Miss4',
children: [
{ value: 'Miss5' },
{ value: 'Miss6', children: [ { value: 'Hit2' } ] }
]
},
{
value: 'Miss7',
children: [
{ value: 'Miss8' },
{ value: 'Miss9', children: [ { value: 'Miss10' } ] }
]
},
{
value: 'Hit3',
children: [
{ value: 'Miss11' },
{ value: 'Miss12', children: [ { value: 'Miss13' } ] }
]
},
{
value: 'Miss14asds',
children: [
{ value: 'Hit4sdas' },
{ value: 'Miss15', children: [ { value: 'Miss16' } ] }
]
},
];
function filter(arr, term) {
var matches = [];
if (!Array.isArray(arr)) return matches;
arr.forEach(function(i) {
if (i.value === term) {
const filterData = (i.children && Array.isArray(i.children))? i.children.filter(values => values.value ===term):[];
console.log(filterData)
i.children =filterData;
matches.push(i);
} else {
// console.log(i.children)
let childResults = filter(i.children, term);
if (childResults.length)
matches.push(Object.assign({}, i, { children: childResults }));
}
})
return matches;
}
const filterData= filter(input,'Miss1');
console.log(filterData)
Below code for filter the parent and child array data using recursive function
const input = [
{
value: 'Miss1',
children: [
{ value: 'Miss2' },
{ value: 'Hit1', children: [ { value: 'Miss3' } ] }
]
},
{
value: 'Miss4',
children: [
{ value: 'Miss5' },
{ value: 'Miss6', children: [ { value: 'Hit2' } ] }
]
},
{
value: 'Miss7',
children: [
{ value: 'Miss8' },
{ value: 'Miss9', children: [ { value: 'Miss10' } ] }
]
},
{
value: 'Hit3',
children: [
{ value: 'Miss11' },
{ value: 'Miss12', children: [ { value: 'Miss13' } ] }
]
},
{
value: 'Miss14',
children: [
{ value: 'Hit4' },
{ value: 'Miss15', children: [ { value: 'Miss16' } ] }
]
},
];
var res = input.filter(function f(o) {
if (o.value.includes("Hit")) return true
if (o.children) {
return (o.children = o.children.filter(f)).length
}
})
console.log(JSON.stringify(res, null, 2))
Here's a function that'll do what you're looking for. Essentially it will test every item in arr
for a match, then recursively call filter on its children
. Also Object.assign
is used so that the underlying object isn't changed.
function filter(arr, term) {
var matches = [];
if (!Array.isArray(arr)) return matches;
arr.forEach(function(i) {
if (i.value.includes(term)) {
matches.push(i);
} else {
let childResults = filter(i.children, term);
if (childResults.length)
matches.push(Object.assign({}, i, { children: childResults }));
}
})
return matches;
}