Tuesday, June 4, 2024
 Popular · Latest · Hot · Upcoming
32
rated 0 times [  38] [ 6]  / answers: 1 / hits: 42855  / 8 Years ago, thu, june 30, 2016, 12:00:00

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:



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' } ] }
]
},
];


I don't know at run time how deep the hierarchy will be, i.e. how many levels of objects will have a children array. I have simplified the example somewhat, I will actually need to match the value properties against an array of search terms. Let's for the moment assume that I am matching where value.includes('Hit').



I need a function that returns a new array, such that:




  • Every non-matching object with no children, or no matches in children hierarchy, should not exist in output object


  • Every object with a descendant that contains a matching object, should remain


  • All descendants of matching objects should remain




I am considering a 'matching object' to be one with a value property that contains the string Hit in this case, and vice versa.



The output should look something like the following:



const expected = [
{
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' },
]
}
];


Many thanks to anyone who took the time to read this far, will post my solution if I get there first.


More From » arrays

 Answers
13

Using .filter() and making a recursive call as I described in the comment above is basically what you need. You just need to update each .children property with the result of the recursive call before returning.



The return value is just the .length of the resulting .children collection, so if there's at least one, the object is kept.



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
}
})




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))








Note that .includes() on a String is ES7, so may need to be patched for legacy browsers. You can use the traditional .indexOf(Hit) != -1 in its place.






To not mutate the original, create a map function that copies an object and use that before the filter.



function copy(o) {
return Object.assign({}, o)
}

var res = input.map(copy).filter(function f(o) {
if (o.value.includes(Hit)) return true

if (o.children) {
return (o.children = o.children.map(copy).filter(f)).length
}
})





To really squeeze the code down, you could do this:



var res = input.filter(function f(o) {
return o.value.includes(Hit) ||
o.children && (o.children = o.children.filter(f)).length
})


Though it gets a little hard to read.


[#61562] Tuesday, June 28, 2016, 8 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
cayden

Total Points: 314
Total Questions: 107
Total Answers: 101

Location: Slovenia
Member since Wed, Apr 6, 2022
2 Years ago
;