I am working on a vue js app and i have the following recursive array :
[
{
id: 1,
name: "sam",
list: [
{
id: 2,
name: "john",
list: []
},
{
id: 6,
name: "sarah",
list: [
{
id: 21,
name: "fadi",
list: []
}
]
}
]
},
{
id: 3,
name: "ross",
list: [
{
id: 4,
name: "maya",
list: []
}
]
},
{
id: 5,
name: "steph",
list: []
},
{
id: 7,
name: "joseph",
list: []
},
],
So i have the id of the object that needs moving and the id of where it should be moved for example i get idmoving=6 and destination = 2 so the object of id 6 with its content should move to the list of the object of id 2 the list becomes the following:
[
{
id: 1,
name: "sam",
list: [
{
id: 2,
name: "john",
list: [
{
id: 6,
name: "sarah",
list: [
{
id: 21,
name: "fadi",
list: []
}
]
}
]
},
]
},
{
id: 3,
name: "ross",
list: [
{
id: 4,
name: "maya",
list: []
}
]
},
{
id: 5,
name: "steph",
list: []
},
{
id: 7,
name: "joseph",
list: []
},
],
I'm sorry i'm still new to this i hope someone can help with a function that can do that.
You could get the source array and index of the wanted node and the target object in advance and splice the array and push the spliced array to the target array.
This approach takes a depth first search and exit the recursion if both nodes are found.
const
move = (data, from, to) => {
const
search = array => array.some((o, index) => {
if (o.id === from) source = { array, index };
else if (o.id === to) target = o;
return source && target || search(o.list);
});
let source, target;
if (!search(data)) return;
target.list.push(...source.array.splice(source.index, 1));
},
data = [{ id: 1, name: "sam", list: [{ id: 2, name: "john", list: [] }, { id: 6, name: "sarah", list: [{ id: 21, name: "fadi", list: [] }] }] }, { id: 3, name: "ross", list: [{ id: 4, name: "maya", list: [] }] }, { id: 5, name: "steph", list: [] }, { id: 7, name: "joseph", list: [] }];
move(data, 6, 2);
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I would probably write this atop some reusable helper functions. Moving a value is a matter of finding it in one location, removing it from there, and adding it to a new location. So I would write a move function that uses find, remove, and add functions.
Each can be done with some fairly simple recursion. Here's one approach:
// Helper functions
const findById = (id, xs) =>
xs .reduce (
(r, x) => r != null ? r : x.id == id ? x : findById (id, x .list) || null,
null
)
const removeById = (id, xs) =>
xs .reduce (
(r, x) => x.id === id ? r : [... r, {... x, list: removeById (id, x .list)}],
[]
)
const addById = (id, newVal, xs) =>
xs .reduce (
(r, x) => [... r, {... x, list: [... addById (id, newVal, x .list), ... (x.id == id ? [newVal] : [])]}],
[]
)
// Main function
const moveById = (id, toId, data) =>
addById (toId, findById (id, data), removeById (id, data))
// Sample data
const data =
[{id: 1, list: [{id: 2, list: [], name: "john"}, {id: 6, list: [{id: 21, list: [], name: "fadi"}], name: "sarah"}], name: "sam"}, {id: 3, list: [{id: 4, list: [], name: "maya"}], name: "ross"}, {id: 5, list: [], name: "steph"}, {id: 7, list: [], name: "joseph"}]
// Demo
console .log (moveById (6, 2, data))
.as-console-wrapper {max-height: 100% !important; top : 0}
This is not complete. Presumably, there needs to be some sort of error-checking about whether a value has been found before we try to add it.
I would actually write these helpers in a more generic fashion, using an arbitrary predicate for input rather than an id. If they were curried, then we could easily create findById by passing the result of applying an id value to a function like (id) => (x) => x.id == id to create a predicate. So in practice, I might write something more like:
const recursiveFind = (pred) => (xs) =>
xs .reduce (
(r, x) => r != null ? r : pred (x) ? x : recursiveFind (pred) (x .list || []) || null,
null
)
const findById = (id) =>
recursiveFind (x => x.id === id)
And something similar for remove and add.
These become useful with, say, recursiveRemove ((x) => x.id > 5) (data), which would remove all elements at any level of nesting with ids greater than 5.
And I might go still further to make your list property parameterizable. But you can see that approach in another SO answer.
Related
I've tried modifying some of the similar solutions on here but I keep getting stuck, I believe I have part of this figured out however, the main caveat is that:
Some of the objects have extra keys, which renders my object comparison logic useless.
I am trying to compare two arrays of objects. One array is the original array, and the other array contains the items I want deleted from the original array. However there's one extra issue in that the second array contains extra keys, so my comparison logic doesn't work.
An example would make this easier, let's say I have the following two arrays:
const originalArray = [{id: 1, name: "darnell"}, {id: 2, name: "funboi"},
{id: 3, name: "jackson5"}, {id: 4, name: "zelensky"}];
const itemsToBeRemoved = [{id: 2, name: "funboi", extraProperty: "something"},
{id: 4, name: "zelensky", extraProperty: "somethingelse"}];
after running the logic, my final output should be this array:
[{id: 1, name: "darnell"}, {id: 3, name: "jackson5"}]
And here's the current code / logic that I have, which compares but doesn't handle the extra keys. How should I handle this? Thank you in advance.
const prepareArray = (arr) => {
return arr.map((el) => {
if (typeof el === "object" && el !== null) {
return JSON.stringify(el);
} else {
return el;
}
});
};
const convertJSON = (arr) => {
return arr.map((el) => {
return JSON.parse(el);
});
};
const compareArrays = (arr1, arr2) => {
const currentArray = [...prepareArray(arr1)];
const deletedItems = [...prepareArray(arr2)];
const compared = currentArray.filter((el) => deletedItems.indexOf(el) === -1);
return convertJSON(compared);
};
How about using filter and some? You can extend the filter condition on select properties using &&.
const originalArray = [
{ id: 1, name: 'darnell' },
{ id: 2, name: 'funboi' },
{ id: 3, name: 'jackson5' },
{ id: 4, name: 'zelensky' },
];
const itemsToBeRemoved = [
{ id: 2, name: 'funboi', extraProperty: 'something' },
{ id: 4, name: 'zelensky', extraProperty: 'somethingelse' },
];
console.log(
originalArray.filter(item => !itemsToBeRemoved.some(itemToBeRemoved => itemToBeRemoved.id === item.id))
)
Or you can generalise it as well.
const originalArray = [
{ id: 1, name: 'darnell' },
{ id: 2, name: 'funboi' },
{ id: 3, name: 'jackson5' },
{ id: 4, name: 'zelensky' },
];
const itemsToBeRemoved = [
{ id: 2, name: 'funboi', extraProperty: 'something' },
{ id: 4, name: 'zelensky', extraProperty: 'somethingelse' },
];
function filterIfSubset(originalArray, itemsToBeRemoved) {
const filteredArray = [];
for (let i = 0; i < originalArray.length; i++) {
let isSubset = false;
for (let j = 0; j < itemsToBeRemoved.length; j++) {
// check if whole object is a subset of the object in itemsToBeRemoved
if (Object.keys(originalArray[i]).every(key => originalArray[i][key] === itemsToBeRemoved[j][key])) {
isSubset = true;
}
}
if (!isSubset) {
filteredArray.push(originalArray[i]);
}
}
return filteredArray;
}
console.log(filterIfSubset(originalArray, itemsToBeRemoved));
Another simpler variation of the second approach:
const originalArray = [
{ id: 1, name: 'darnell' },
{ id: 2, name: 'funboi' },
{ id: 3, name: 'jackson5' },
{ id: 4, name: 'zelensky' },
];
const itemsToBeRemoved = [
{ id: 2, name: 'funboi', extraProperty: 'something' },
{ id: 4, name: 'zelensky', extraProperty: 'somethingelse' },
];
const removeSubsetObjectsIfExists = (originalArray, itemsToBeRemoved) => {
return originalArray.filter(item => {
const isSubset = itemsToBeRemoved.some(itemToBeRemoved => {
return Object.keys(item).every(key => {
return item[key] === itemToBeRemoved[key];
});
});
return !isSubset;
});
}
console.log(removeSubsetObjectsIfExists(originalArray, itemsToBeRemoved));
The example below is a reusable function, the third parameter is the key to which you compare values from both arrays.
Details are commented in example
const arr=[{id:1,name:"darnell"},{id:2,name:"funboi"},{id:3,name:"jackson5"},{id:4,name:"zelensky"}],del=[{id:2,name:"funboi",extraProperty:"something"},{id:4,name:"zelensky",extraProperty:"somethingelse"}];
/** Compare arrayA vs. delArray by a given key's value.
--- ex. key = 'id'
**/
function deleteByKey(arrayA, delArray, key) {
/* Get an array of only the values of the given key from delArray
--- ex. delList = [1, 2, 3, 4]
*/
const delList = delArray.map(obj => obj[key]);
/* On every object of arrayA compare delList values vs
current object's key's value
--- ex. current obj[id] = 2
--- [1, 2, 3, 4].includes(obj[id])
Any match returns an empty array and non-matches are returned
in it's own array.
--- ex. ? [] : [obj]
The final return is a flattened array of the non-matching objects
*/
return arrayA.flatMap(obj => delList.includes(obj[key]) ? [] : [obj]);
};
console.log(deleteByKey(arr, del, 'id'));
let ff = [{ id: 1, name: 'darnell' }, { id: 2, name: 'funboi' },
{ id: 3, name: 'jackson5' },
{ id: 4, name: 'zelensky' }]
let cc = [{ id: 2, name: 'funboi', extraProperty: 'something' },
{ id: 4, name: 'zelensky', extraProperty: 'somethingelse' }]
let ar = []
let out = []
const result = ff.filter(function(i){
ar.push(i.id)
cc.forEach(function(k){
out.push(k.id)
})
if(!out.includes(i.id)){
// console.log(i.id, i)
return i
}
})
console.log(result)
Assuming that I have 2 multidimensional arrays of objects
const products = [
{
id: 1
name: 'lorem'
},
{
id: 3,
name: 'ipsum'
}
];
const tmp_products = [
{
id: 1
name: 'lorem'
},
{
id: 14,
name: 'porros'
},
{
id: 3,
name: 'ipsum'
},
{
id: 105,
name: 'dolor'
},
{
id: 32,
name: 'simet'
}
];
What is the correct way to find the missing indexes by id property?
I'm expecting an output such as [1,3,4] since those objects are not present in products
I found a similar question but applied to plain arrays:
Javascript find index of missing elements of two arrays
var a = ['a', 'b', 'c'],
b = ['b'],
result = [];
_.difference(a, b).forEach(function(t) {result.push(a.indexOf(t))});
console.log(result);
I'd like to use ES6 or lodash to get this as short as possible
You can use sets to do it quickly:
const productIds = new Set(products.map(v => v.id));
const inds = tmp_products
.map((v, i) => [v, i])
.filter(([v, i]) => !productIds.has(v.id))
.map(([v, i]) => i);
inds // [1, 3, 4]
You can use Array.prototype.reduce function to get the list of missing products' index.
Inside reduce callback, you can check if the product is included in products array or not using Array.prototype.some and based on that result, you can decide to add the product index or not.
const products = [
{
id: 1,
name: 'lorem'
},
{
id: 3,
name: 'ipsum'
}
];
const tmp_products = [
{
id: 1,
name: 'lorem'
},
{
id: 14,
name: 'porros'
},
{
id: 3,
name: 'ipsum'
},
{
id: 105,
name: 'dolor'
},
{
id: 32,
name: 'simet'
}
];
const missingIndex = tmp_products.reduce((acc, curV, curI) => {
if (!products.some((item) => item.id === curV.id && item.name === curV.name)) {
acc.push(curI);
}
return acc;
}, []);
console.log(missingIndex);
With lodash you could use differenceWith:
_(tmp_products)
.differenceWith(products, _.isEqual)
.map(prod => tmp_products.indexOf(prod))
.value()
This may not be great for performance, but it depends on how many items you have. With the size of your arrays this should perform ok.
I have array of array of object as follows:
[
[
{
id: 1,
itemName: 'xxx',
...
},
{
id: 1,
itemName: 'yyy',
...
},
...
],
[
{
id: 2,
itemName: 'aaa',
...
},
{
id: 2,
itemName: 'kkk',
...
},
...
],
[
{
id: 3,
itemName: 'kkk',
...
},
{
id: 3,
itemName: 'yyy',
...
},
...
]
]
I am trying to check if any itemName from objects inside arrays equals given string, but I stuck at the solution that keeps these arrays with such object in one array. Here is my solution:
function isNameAcrossData(givenString){
return arr.map(arrItem =>
arrItem.find(item => item.itemId === givenString)
);
}
My solution doesn't return boolean but just one array with objects, that contain givenString and undefined as last array element. How to modify it to return just true/false value?
Use a .some inside a .some, to see if some of the arrays have at least one element inside matching the condition:
const isNameAcrossData = givenString => arr.some(
subarr => subarr.some(
({ itemName }) => itemName === givenString
)
);
const arr=[[{id:1,itemName:"xxx"},{id:1,itemName:"yyy"}],[{id:2,itemName:"aaa"},{id:2,itemName:"kkk"}],[{id:3,itemName:"kkk"},{id:3,itemName:"yyy"}]];
console.log(isNameAcrossData('xxx'));
console.log(isNameAcrossData('doesntexist'));
You could also flatten the outer array first:
const isNameAcrossData = givenString => arr.flat().some(
({ itemName }) => itemName === givenString
);
const arr=[[{id:1,itemName:"xxx"},{id:1,itemName:"yyy"}],[{id:2,itemName:"aaa"},{id:2,itemName:"kkk"}],[{id:3,itemName:"kkk"},{id:3,itemName:"yyy"}]];
console.log(isNameAcrossData('xxx'));
console.log(isNameAcrossData('doesntexist'));
You could check with some and return an array of boolean with using the wanted property.
function mapHasValue(key, value) {
return data.map(array => array.some(item => item[key] === value));
}
var data = [[{ id: 1, itemName: 'xxx' }, { id: 1, itemName: 'yyy' }], [{ id: 2, itemName: 'aaa' }, { id: 2, itemName: 'kkk' }], [{ id: 3, itemName: 'kkk' }, { id: 3, itemName: 'yyy' }]];
console.log(mapHasValue('id', 3));
Your code returns
[undefined, undefined, undefined]
because map returns an array so this approach won't work
You have first to loop through all the data and check inside then outside the loop assign to some variable true if there is a match.
Basically you have to return after you loop the data.
Working example for both cases:
const arr=[[{id:1,itemName:"xxx"},{id:1,itemName:"yyy"}],[{id:2,itemName:"aaa"},{id:2,itemName:"kkk"}],[{id:3,itemName:"kkk"},{id:3,itemName:"yyy"}]];
function isNameAcrossData(givenString){
let isMatch = false;
arr.map(childArr => {
childArr.map(obj => obj.itemName === givenString ? isMatch = true : null);
});
return isMatch;
}
console.log(isNameAcrossData('kkk'));
console.log(isNameAcrossData('bbb'));
I just learned a new trick to find the same object in 2 array object, it works very well. it uses array.filter and array.some, as code bellow, but I don't understand how filter() can run when some() will return true or false.
const similarity = (arr, values) => arr.filter(item => values.some(m => (m.id === item.id) && (m.name === item.name)));
my input:
let arr1 = [
{
id: 1,
name: "kiet"
},
{
id: 2,
name: 'phan'
},
{
id: 3,
name: 'tuan'
}]
let arr2 = [
{
id: 1,
name: "kiet"
},
{
id: 2,
name: 'haha'
},
{
id: 5,
name: 'tuan'
}
]
my result :
[ { id: 1, name: 'kiet' } ]
You are Filtering the array, then passing this condition inside the some
(m.id === item.id) && (m.name === item.name)
If the id of the second array (here, m is the second array's objects) is equal to the first array's id (here, item is the array's objects) and the name is equal to the first array's name property. If yes then it will return true. So the filter gets a true and if is does, it will return that particular object for which it gets a true
let arr1 = [{ id: 1, name: "kiet" }, { id: 2, name: 'phan' }, { id: 3, name: 'tuan'}]
let arr2 = [ { id: 1, name: "kiet" }, { id: 2, name: 'haha' }, { id: 5, name: 'tuan' } ]
const similarity = (arr, values) => arr.filter(item => values.some(m => (m.id === item.id) && (m.name === item.name)));
console.log(similarity(arr1,arr2))
You get the elements from the array which have the same properties id/name as the values array.
Array#filter needs a (kind of) boolean value and if true or truthy, then the item is taken to the new array.
Array#some checks an item and if the callback returns a truthy value, then it returns true, if not then false.
const
similarity = (array, values) => array.filter(item => values.some(m => m.id === item.id && m.name === item.name)),
arr1 = [ { id: 1, name: "kiet" }, { id: 2, name: 'phan' }, { id: 3, name: 'tuan' }],
arr2 = [ { id: 1, name: "kiet" }, { id: 2, name: 'haha' }, { id: 5, name: 'tuan' }],
result = similarity(arr1, arr2);
console.log(result);
For larger data sets, you could take a Set, this is iterated once with a combined value and checked against for filtering.
const
similarity = (array, values) => {
const
getKey = ({ id, name }) => [id, name].join('|'),
keys = new Set(values.map(getKey));
return array.filter(o => keys.has(getKey(o)));
},
arr1 = [ { id: 1, name: "kiet" }, { id: 2, name: 'phan' }, { id: 3, name: 'tuan' }],
arr2 = [ { id: 1, name: "kiet" }, { id: 2, name: 'haha' }, { id: 5, name: 'tuan' }],
result = similarity(arr1, arr2);
console.log(result);
This is a good technique if you want to check for array intersection.
const vowels = [...'AEIOU'];
const isVowel = x => vowels.some(v => v === x);
const hasVowel = string => [...string].filter(isVowel).length > 0;
I need to filter objects recursively in a deeply nested array of objects using javascript, maybe with the help of lodash.
What is the cleanest way to do it, If I don't know how many nested object there will be in my array?
Let's say I have the following structure
[
{
label: "first",
id: 1,
children: []
},
{
label: "second",
id: 2,
children: [
{
label: "third",
id: 3,
children: [
{
label: "fifth",
id: 5,
children: []
},
{
label: "sixth",
id: 6,
children: [
{
label: "seventh",
id: 7,
children: []
}
]
}
]
},
{
label: "fourth",
id: 4,
children: []
}
]
}
];
I want to find the one with id 6, and if it has children return true otherwise false.
Of course If I have a similar data structure but with different number of items it should work too.
Since you only want a true of false answer you can use some() on the recursion, effectively doing a depth-first search, and make it pretty succinct:
let arr = [{label: "first",id: 1,children: []},{label: "second",id: 2,children: [{label: "third",id: 3,children: [{label: "fifth",id: 5,children: []},{label: "sixth",id: 6,children: [{label: "seventh",id: 7,children: []}]}]},{label: "fourth",id: 4,children: []}]}];
function findNested(arr, id) {
let found = arr.find(node => node.id === id)
return found
? found.children.length > 0
: arr.some((c) => findNested(c.children, id))
}
console.log(findNested(arr, 6)) // True: found with children
console.log(findNested(arr, 7)) // False: found no children
console.log(findNested(arr, 97)) // False: not found
Perhaps a recursive solution along the lines of this might work for you? Here, the node with supplied id is recursively searched for through the 'children' of the supplied input data. If a child node with matching id is found, a boolean result is returned based on the existence of data in that nodes children array:
function nodeWithIdHasChildren(children, id) {
for(const child of children) {
// If this child node matches supplied id, then check to see if
// it has data in it's children array and return true/false accordinly
if(child.id === id) {
if(Array.isArray(child.children) && child.children.length > 0) {
return true
}
else {
return false
}
}
else {
const result = nodeWithIdHasChildren(child.children, id);
// If result returned from this recursion branch is not undefined
// then assume it's true or false from a node matching the supplied
// id. Pass the return result up the call stack
if(result !== undefined) {
return result
}
}
}
}
const data = [
{
label: "first",
id: 1,
children: []
},
{
label: "second",
id: 2,
children: [
{
label: "third",
id: 3,
children: [
{
label: "fifth",
id: 5,
children: []
},
{
label: "sixth",
id: 6,
children: [
{
label: "seventh",
id: 7,
children: []
}
]
}
]
},
{
label: "fourth",
id: 4,
children: []
}
]
}
];
console.log('node 6 has children:', nodeWithIdHasChildren( data, 6 ) )
console.log('node 7 has children:', nodeWithIdHasChildren( data, 7 ) )
console.log('node 100 has children:', nodeWithIdHasChildren( data, 7 ), '(because node 100 does not exist)' )
Here is another solution using recursion and doing it via only one Array.find:
const data = [ { label: "first", id: 1, children: [] }, { label: "second", id: 2, children: [ { label: "third", id: 3, children: [ { label: "fifth", id: 5, children: [] }, { label: "sixth", id: 6, children: [ { label: "seventh", id: 7, children: [] } ] } ] }, { label: "fourth", id: 4, children: [] } ] } ];
const search = (data, id) => {
var f, s = (d, id) => d.find(x => x.id == id ? f = x : s(x.children, id))
s(data, id)
return f ? f.children.length > 0 : false
}
console.log(search(data, 6)) // True: found with children
console.log(search(data, 7)) // False: found but has no children
console.log(search(data, 15)) // False: not found at all
The idea is to have a recursive function which when finds the id remembers the object.
Once we have the found (or we know we do not have an entry found) just return the children array length or return false.
If you want to actually return the found object instead of the boolean for children.length:
const data = [ { label: "first", id: 1, children: [] }, { label: "second", id: 2, children: [ { label: "third", id: 3, children: [ { label: "fifth", id: 5, children: [] }, { label: "sixth", id: 6, children: [ { label: "seventh", id: 7, children: [] } ] } ] }, { label: "fourth", id: 4, children: [] } ] } ];
const search = (data, id) => {
var f, s = (d, id) => d.find(x => x.id == id ? f = x : s(x.children, id))
s(data, id)
return f
}
console.log(search(data, 6)) // returns only the object with id:6
console.log(search(data, 7)) // returns only the object with id: 7
console.log(search(data, 71)) // returns undefined since nothing was found
You can use "recursion" like below to check if id has children or not
let arr = [{label: "first",id: 1,children: []},{label: "second",id: 2,children: [{label: "third",id: 3,children: [{label: "fifth",id: 5,children: []},{label: "sixth",id: 6,children: [{label: "seventh",id: 7,children: []}]}]},{label: "fourth",id: 4,children: []}]}];
function hasChildren(arr, id) {
let res = false
for (let d of arr) {
if(d.id == id) return d.children.length > 0
res = res || hasChildren(d.children, id)
if(res) return true
}
return res
}
console.log('id 4 has children? ', hasChildren(arr, 4))
console.log('id 6 has children? ', hasChildren(arr, 6))
You can do it using three simple javascript functions:
// Function to Flatten results
var flattenAll = function(data) {
var result = [];
var flatten = function(arr) {
_.forEach(arr, function(a) {
result.push(a);
flatten(a.children);
});
};
flatten(data);
return result;
};
// Function to search on flattened array
var search = function(flattened, id) {
var found = _.find(flattened, function(d) {
return d.id == id;
});
return found;
};
// Function to check if element is found and have children
var hasChildren = function(element) {
return element && element.children && element.children.length > 0;
}
// Usage, search for id = 6
hasChildren(search(flattenAll(your_data_object), 6))
Plunker
You can use a generator function to iterate the nodes recursively and simplify your logic for checking existence by using Array.prototype.some():
const data = [{label:'first',id:1,children:[]},{label:'second',id:2,children:[{label:'third',id:3,children:[{label:'fifth',id:5,children:[]},{label:'sixth',id:6,children:[{label:'seventh',id:7,children:[]}]}]},{label:'fourth',id:4,children:[]}]}];
function * nodes (array) {
for (const node of array) {
yield node;
yield * nodes(node.children);
}
}
const array = Array.from(nodes(data));
console.log(array.some(node => node.id === 6 && node.children.length > 0));
console.log(array.some(node => node.id === 7 && node.children.length > 0));
The JSON.parse reviver parameter or the JSON.stringify replacer parameter can be used to check all values, and generate flat id lookup object with references to the nodes :
var lookup = {}, json = '[{"label":"first","id":1,"children":[]},{"label":"second","id":2,"children":[{"label":"third","id":3,"children":[{"label":"fifth","id":5,"children":[]},{"label":"sixth","id":6,"children":[{"label":"seventh","id":7,"children":[]}]}]},{"label":"fourth","id":4,"children":[]}]}]'
var result = JSON.parse(json, (key, val) => val.id ? lookup[val.id] = val : val);
console.log( 'id: 2, children count:', lookup[2].children.length )
console.log( 'id: 6, children count:', lookup[6].children.length )
console.log( lookup )
I suggest to use deepdash extension for lodash:
var id6HasChildren = _.filterDeep(obj,
function(value, key, parent) {
if (key == 'children' && parent.id == 6 && value.length) return true;
},
{ leavesOnly: false }
).length>0;
Here is a docs for filterDeep.
And this a full test for your case.
We now use object-scan for data processing needs like this. It's very powerful once you wrap your head around it. This is how you could solve your questions
// const objectScan = require('object-scan');
const hasChildren = (e) => e instanceof Object && Array.isArray(e.children) && e.children.length !== 0;
const find = (id, input) => {
const match = objectScan(['**'], {
abort: true,
rtn: 'value',
filterFn: ({ value }) => value.id === id
})(input);
return hasChildren(match);
};
const data = [{ label: 'first', id: 1, children: [] }, { label: 'second', id: 2, children: [{ label: 'third', id: 3, children: [{ label: 'fifth', id: 5, children: [] }, { label: 'sixth', id: 6, children: [{ label: 'seventh', id: 7, children: [] }] }] }, { label: 'fourth', id: 4, children: [] }] }];
console.log(find(6, data));
// => true
console.log(find(2, data));
// => true
console.log(find(7, data));
// => false
console.log(find(999, data));
// => false
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan