How to get the value of each node of the tree structure - javascript

An array of nested arrays and objects, each node has a unique value, finding a value on this data, how to get the value on each node?
const opts = [
{
value: '01',
children: [
{ value: '0198' },
{ value: '0195', children: [{ value: '09977' }] }
]
},
{
value: '02',
children: [
{ value: '01986' },
{
value: '0195',
children: [
{ value: '09978', children: [{ value: '09864' }, { value: '90876' }] }
]
}
]
}
];
const code = '90876';
// expected get an array ['02','0195','09978','90876']

U could use a function to walk over the object structure recursively like described here:
const opts = [
{
value: '01',
children: [
{ value: '0198' },
{ value: '0195', children: [{ value: '09977' }] }
]
},
{
value: '02',
children: [
{ value: '01986' },
{
value: '0195',
children: [
{ value: '09978', children: [{ value: '09864' }, { value: '90876' }] }
]
}
]
}
];
function eachRecursive(obj, cb) {
for (var k in obj) {
if (typeof obj[k] == "object" && obj[k] !== null)
eachRecursive(obj[k], cb);
else
cb(obj[k]);
}
}
let results = [];
eachRecursive(opts, val => results.push(val));
console.log(results);
but not sure what you mean with your comment: // expected get an array ['02','0195','0997','90876'] can your explain why you expect that?

you can use a dfs algo
function dfs(o, target){
if(o.value == target) return [target];
if(!o.children) return false;
let path;
o.children.find(x=>path=dfs(x, target));
if(path){
return [o.value].concat(path);
}
};
const opts = [
{
value: '01',
children: [
{ value: '0198' },
{ value: '0195', children: [{ value: '09977' }] }
]
},
{
value: '02',
children: [
{ value: '01986' },
{
value: '0195',
children: [
{ value: '09978', children: [{ value: '09864' }, { value: '90876' }] }
]
}
]
}
];
let path;
opts.find(x=>path=dfs(x, '90876'))
console.log(path);

const opts = [
{
value: '01',
children: [
{ value: '0198' },
{ value: '0195', children: [{ value: '09977' }] }
]
},
{
value: '02',
children: [
{ value: '01986' },
{
value: '0195',
children: [
{ value: '09978', children: [{ value: '09864' }, { value: '90876' }] }
]
}
]
}
];
console.log(opts[1].value)
console.log(opts[1].children[1].value)
console.log(opts[1].children[1].children[0].value)
console.log(opts[1].children[1].children[0].children[1].value)

Related

How to filter array of array objects?

Trying to filter array of array objects. when variable matches with array of object value of dropDownOne key 'filterValue', then it will return dropDownTwo array,
let testName = ‘ filterValue’
var nestedArray = [
[
{
dropDownOne: {
key: "filterValue",
value: "test1"
},
dropDownTwo: [
{
key: "retrieveArrKey1",
value: "test123"
},
{
key: "retrieveArrKey2",
value: "test345"
}
]
}
],
[
{
dropDownOne: {
key: "NoFilter",
value: "test2"
},
dropDownTwo: [
{
key: "dropDown2",
value: "test"
},
{
key: "dropDown3",
value: "test"
}
]
}
]
]
Output =
dropDownTwo:[
{
key: "retrieveArrKey1",
value: "test123"
},
{
key: "retrieveArrKey2",
value: "test345"
}
]
Tried with this
let filterObj = nestedArray.filter((arr => arr.filter(value => {
if (value[0].dropDownOne.key === 'filterValue') {
return arr[1];
}
}))
But did not get the correct result
We can use Array.flat() and Array.flatMap() combined with Array.filter() to do it
let result = nestedArray.flat().filter(e => e.dropDownOne.key === testName).flatMap(e => e.dropDownTwo)
console.log(result)
let testName = `filterValue`
var nestedArray = [
[
{
dropDownOne: {
key: "filterValue",
value: "test1"
},
dropDownTwo: [
{
key: "retrieveArrKey1",
value: "test123"
},
{
key: "retrieveArrKey2",
value: "test345"
}
]
}
],
[
{
dropDownOne: {
key: "NoFilter",
value: "test2"
},
dropDownTwo: [
{
key: "dropDown2",
value: "test"
},
{
key: "dropDown3",
value: "test"
}
]
}
]
]
let result = nestedArray.flat().filter(e => e.dropDownOne.key === testName).flatMap(e => e.dropDownTwo)
console.log(result)
You can do it in this way :
nestedArray.filter(arr => arr.find(item=> item.dropDownOne.key === 'filterValue'))[0][0].dropDownTwo
var nestedArray = [
[
{
dropDownOne: {
key: "filterValue",
value: "test1"
},
dropDownTwo: [
{
key: "retrieveArrKey1",
value: "test123"
},
{
key: "retrieveArrKey2",
value: "test345"
}
]
}
],
[
{
dropDownOne: {
key: "NoFilter",
value: "test2"
},
dropDownTwo: [
{
key: "dropDown2",
value: "test"
},
{
key: "dropDown3",
value: "test"
}
]
}
]
]
const result = nestedArray.filter(arr => arr.find(item=> item.dropDownOne.key === 'filterValue'))[0][0].dropDownTwo
console.log(result)
You could use both flat() to flatten the nestedArray, then use the find() method to find the object with a dropDownOne.key that matches the value of testName.
it should then return the dropDownTwo property of that object.
let result = nestedArray.flat().find(obj => obj.dropDownOne.key === testName);
let filterObj = result ? result.dropDownTwo : [];
console.log(filterObj);
a full running example:
let testName = 'filterValue';
var nestedArray = [
[{
dropDownOne: {
key: "filterValue",
value: "test1"
},
dropDownTwo: [{
key: "retrieveArrKey1",
value: "test123"
},
{
key: "retrieveArrKey2",
value: "test345"
}
]
}],
[{
dropDownOne: {
key: "NoFilter",
value: "test2"
},
dropDownTwo: [{
key: "dropDown2",
value: "test"
},
{
key: "dropDown3",
value: "test"
}
]
}]
]
let result = nestedArray.flat().find(obj => obj.dropDownOne.key === testName);
let filterObj = result ? result.dropDownTwo : [];
console.log(filterObj);
Don't like other solutions due to too many array iterations. You could make a single run using reduce function which is a grandfather of many JS Array functions. It looks a bit worse than flat-filter-flat-..., but will probably work way faster due to much lower array iterations.
const testName = 'filterValue';
const nestedArray = [
[{
dropDownOne: {key: 'filterValue', value: 'test1'},
dropDownTwo: [
{key: 'retrieveArrKey1', value: 'test123'},
{key: 'retrieveArrKey2', value: 'test345'},
],
}],
[{
dropDownOne: {key: 'NoFilter', value: 'test2'},
dropDownTwo: [
{key: 'dropDown2', value: 'test'},
{key: 'dropDown3', value: 'test'},
],
}],
];
const result = nestedArray.reduce((acc, nestedItem) => {
nestedItem.forEach(item => {
if (item.dropDownOne.key === testName) {
acc.push(item.dropDownTwo);
}
});
return acc;
}, []);
console.log(result);

Compare two array of objects based on a properties value and return the matched

HI All I am having two array of object my aim is to compare them and filter out the matched result
my data looks like this
let data1 = [
{
name:'tom',
process:'flipkart',
master:'pharma',
profiles: [
{
level:'begginer',
language:'hindi',
role:['flp_admin','flp_teacher']
}
]
},
{
name:'jeo',
process:'amazon',
master:'science',
profiles: [
{
level:'begginer',
language:'english',
role:['amz_admin']
}
]
},
{
name:'jerry',
process:'email',
master:'it',
profiles: [
{
level:'begginer',
language:'urdu',
role:['eml_teacher']
}
]
}
]
let data2 = [
{
masterName:'Pharma',
businessProcess: [
{ label:'flipkart', value:'flipkart' },
{ label:'amazon', value:'amazon' }
]
},
{
masterName:'science',
businessProcess: [
{ label:'flipkart', value:'flipkart' },
{ label:'amazon', value:'amazon' }
]
},
{
masterName:'it',
businessProcess: [
{ label:'email', value:'email' },
{ label:'amazon', value:'amazon' }
]
}
I want to compare data1 with data2 and return the match from data2 if master of data1 matches with masterName of data2 and if business of data1 matches with businessProcess.label of data2.
Could anyone please tell me how can I do it?
You can use Array.filter and Array.find to loop over and find the matching items:
let data1 = [{
name: 'tom',
process: 'flipkart',
master: 'pharma',
profiles: [{
level: 'begginer',
language: 'hindi',
role: ['flp_admin', 'flp_teacher']
}]
},
{
name: 'jeo',
process: 'amazon',
master: 'science',
profiles: [{
level: 'begginer',
language: 'english',
role: ['amz_admin']
}]
},
{
name: 'jerry',
process: 'email',
master: 'it',
profiles: [{
level: 'begginer',
language: 'urdu',
role: ['eml_teacher']
}]
}
]
let data2 = [{
masterName: 'Pharma',
businessProcess: [{
label: 'flipkart',
value: 'flipkart'
},
{
label: 'amazon',
value: 'amazon'
}
]
},
{
masterName: 'science',
businessProcess: [{
label: 'flipkart',
value: 'flipkart'
},
{
label: 'amazon',
value: 'amazon'
}
]
},
{
masterName: 'it',
businessProcess: [{
label: 'email',
value: 'email'
},
{
label: 'amazon',
value: 'amazon'
}
]
}
];
console.log(data1.filter((d) => {
return data2.find((d2) => {
//check if data matername equals data1 master
// or if data1.process value exists in one of the item of businessProcess as value
return d2.masterName == d.master || d2.businessProcess.find(b => b.value === d.process);
});
}));

merge javascript array values with same key

What is the best way to re-organize array into output? I need to merge the values with the same key into an array of objects.
the input array:
var array = [
{
aaa: {
name: "foo1",
value: "val1"
}
},
{
aaa: {
name: "foo2",
value: "val2"
}
},
{
bbb: {
name: "foo3",
value: "val3"
}
},
{
bbb: {
name: "foo4",
value: "val4"
}
}
];
The desired output:
var output = [
{
aaa:
[{
name: "foo1",
value: "val1"
},{
name: "foo2",
value: "val2"
}]
},
{
bbb:
[{
name: "foo3",
value: "val3"
},{
name: "foo4",
value: "val4"
}]
}
];
What would be the best way to achieve this?
Thanks in advance.
You can use reduce
var array = [{aaa: {name: "foo1",value: "val1"}},{aaa: {name: "foo2",value: "val2"}},{bbb: {name: "foo3",value: "val3"}},{bbb: {name: "foo4",value: "val4"}}];
var newArray = array.reduce((c, v) => {
for (var k in v) {
c[k] = c[k] || [];
c[k].push(v[k]);
}
return c;
}, {});
console.log(newArray);
Doc: reduce()
You can use reduce and forEach function.
var array = [{ aaa: { name: "foo1", value: "val1" } }, { aaa: { name: "foo2", value: "val2" } }, { bbb: { name: "foo3", value: "val3" } }, { bbb: { name: "foo4", value: "val4" } }];
var result = array.reduce((a, c) => {
Object.keys(c).forEach((k) => {
if (a[k]) {
a[k].push(c[k]);
} else {
a[k] = [c[k]];
}
})
return a;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Search from an array of objects via array of string to get array of objects

Hie All,
I have two arrays as below:
var arr1 = [ '1956888670', '2109171907', '298845084' ];
var arr2 = [
{ KEY: '1262875245', VALUE: 'Vijay Kumar Verma' },
{ KEY: '1956888670', VALUE: 'Sivakesava Nallam' },
{ KEY: '2109171907', VALUE: 'udm analyst' },
{ KEY: '298845084', VALUE: 'Mukesh Nagora' },
{ KEY: '2007285563', VALUE: 'Yang Liu' },
{ KEY: '1976156380', VALUE: 'Imtiaz Zafar' },
];
arr1 has keys and arr2 has key and value.
i want to output key and value of only those keys which are present in arr1.
Hence my output should be,
[{ KEY: '1956888670', VALUE: 'Sivakesava Nallam' },
{ KEY: '2109171907', VALUE: 'udm analyst' },
{ KEY: '298845084', VALUE: 'Mukesh Nagora' },]
Requesting your help to write a function to fetch required output using javascript.Thanks in advance.
var arr1 = [ '1956888670', '2109171907', '298845084' ];
var arr2 = [
{ KEY: '1262875245', VALUE: 'Vijay Kumar Verma' },
{ KEY: '1956888670', VALUE: 'Sivakesava Nallam' },
{ KEY: '2109171907', VALUE: 'udm analyst' },
{ KEY: '298845084', VALUE: 'Mukesh Nagora' },
{ KEY: '2007285563', VALUE: 'Yang Liu' },
{ KEY: '1976156380', VALUE: 'Imtiaz Zafar' },
];
result = arr2.filter( function(obj) {
return arr1.indexOf(obj.KEY) >= 0;
});
console.info(result);
You can try with filter - Array.prototype.filter():
var output = arr2.filter(function(item) {
return arr1.indexOf(item.KEY) >= 0;
});
You could use Set for the filtering.
var arr1 = ['1956888670', '2109171907', '298845084'],
arr2 = [{ KEY: '1262875245', VALUE: 'Vijay Kumar Verma' }, { KEY: '1956888670', VALUE: 'Sivakesava Nallam' }, { KEY: '2109171907', VALUE: 'udm nalyst' }, { KEY: '298845084', VALUE: 'Mukesh Nagora' }, { KEY: '2007285563', ALUE: 'Yang Liu' }, { KEY: '1976156380', VALUE: 'Imtiaz Zafar' }],
mySet = new Set,
result;
arr1.forEach(function (a) {
mySet.add(a);
});
result = arr2.filter(function (a) {
return mySet.has(a.KEY);
});
console.log(result);

Recursively filter array of objects

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.
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.
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;
}
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);
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 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));
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"
}
]
}
]
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
Here is a different type of solution using object-scan.
This solution is iterative instead of recursive. It works because object-scan iterates in delete safe order. Basically we traverse into the tree and break on any match. Then we keep track of matches at the different depth (ensuring that we reset that information appropriately as we traverse into a neighbouring branches).
It's mostly an academic exercise as the recursive approach is cleaner and faster. However this answer might be interesting if there is additional processing that needs to be done or stack depth errors are a problem.
// const objectScan = require('object-scan');
const myInput = [{ 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 myFilterFn = ({ value }) => value.includes('Hit');
const rewrite = (input, filterFn) => objectScan(['**(^children$)'], {
breakFn: ({ isMatch, value }) => isMatch && filterFn(value),
filterFn: ({
parent, property, key, value, context
}) => {
const len = (key.length - 1) / 2;
if (context.prevLen <= len) {
context.matches.length = context.prevLen + 1;
}
context.prevLen = len;
if (context.matches[len + 1] === true || filterFn(value)) {
context.matches[len] = true;
return false;
}
parent.splice(property, 1);
return true;
},
useArraySelector: false,
rtn: 'count'
})(input, { prevLen: 0, matches: [] });
console.log(rewrite(myInput, myFilterFn)); // returns number of deletions
// => 8
console.log(myInput);
// => [ { 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' } ] } ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#16.0.0"></script>
Disclaimer: I'm the author of object-scan
Was looking for another way to solve this problem without directly mutating the children array and came up with 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' } ] }
]
},
];
const filtered = input.reduce(function fr(acc, curr) {
if (curr.children) {
const children = curr.children.reduce(fr, []);
if (children.length) {
return acc.concat({ ...curr, children: children });
}
}
if (curr.value.includes('Hit')) {
return acc.concat(curr);
} else {
return acc;
}
}, []);
console.log(filtered);

Categories