Find node in javascript tree by some params - javascript

I try to write mixin for underscore, which can find node by some params, for example:
_.findDeep(tree, {id: 5456, parent_id: 555})
Tree:
var tree = [
{
id: 22,
name: 'qqqq',
depth: 0,
parent_id: 11,
children: [
{
id: 222,
name: 'ttttt',
depth: 1,
parent_id: 444,
children: [],
positions: []
},
{
id: 5456,
name: 'yyyy',
depth: 1,
parent_id: 555,
children: [
{
id: 6767,
name: 'dfgfdg',
depth: 3,
parent_id: 6564,
children: [],
positions: []
},
{
id: 4345,
name: 'dfgdgfg',
depth: 3,
parent_id: 45234,
children: [],
positions: []
},
],
positions: []
},
],
positions: [
{
id: 14,
name: 'rere',
price: 20
},
{
id: 12,
name: 'tttyty',
price: 30
},
]
},
{
id: 33,
name: 'wwww',
depth: 0,
parent_id: 22,
children: [],
positions: []
},
{
id: 44,
name: 'eeee',
depth: 0,
parent_id: 33,
children: [],
positions: []
},
]
Wrong function, which alaways returns 'undefined', but console.log display founded node:
_.mixin({
findDeep: function(items, attrs) {
var key, n_key, n_value, result, value;
result = _.findWhere(items, attrs);
console.log(items, result, _.isUndefined(result));
if (_.isUndefined(result)) {
for (key in items) {
value = items[key];
for (n_key in value) {
n_value = value[n_key];
if (_.isObject(n_value) || _.isArray(n_value)) {
result = _.findDeep(n_value, attrs);
if (!_.isUndefined(result)) {
return result;
}
}
}
}
}
return result;
}
});
where is mistake? please help me

In your code, your
for (n_key in value) {
n_value = value[n_key];
if (_.isObject(n_value) || _.isArray(n_value)) {
_.findDeep(n_value, attrs);
}
}
is doing a deep search, but doesn't return any result. You should assign the result to the search, and if the result is not undefined, return it or break the for loop imediately.
So it becomes:
_.mixin({
findDeep: function(items, attrs) {
var key, n_key, n_value, result, value;
result = _.findWhere(items, attrs);
console.log(items, result, _.isUndefined(result));
if (_.isUndefined(result)) {
for (key in items) {
value = items[key];
for (n_key in value) {
n_value = value[n_key];
if (_.isObject(n_value) || _.isArray(n_value)) {
result = _.findDeep(n_value, attrs);
}
// Once you find the result, you can return the founded result
if (!_.isUndefined(result)) {
return result;
}
}
}
}
return result;
}
});
Snippet to show the correct result:
var tree = [
{
id: 22,
name: 'qqqq',
depth: 0,
parent_id: 11,
children: [
{
id: 222,
name: 'ttttt',
depth: 1,
parent_id: 444,
children: [],
positions: []
},
{
id: 5456,
name: 'yyyy',
depth: 1,
parent_id: 555,
children: [
{
id: 6767,
name: 'dfgfdg',
depth: 3,
parent_id: 6564,
children: [],
positions: []
},
{
id: 4345,
name: 'dfgdgfg',
depth: 3,
parent_id: 45234,
children: [],
positions: []
},
],
positions: []
},
],
positions: [
{
id: 14,
name: 'rere',
price: 20
},
{
id: 12,
name: 'tttyty',
price: 30
},
]
},
{
id: 33,
name: 'wwww',
depth: 0,
parent_id: 22,
children: [],
positions: []
},
{
id: 44,
name: 'eeee',
depth: 0,
parent_id: 33,
children: [],
positions: []
},
];
_.mixin({
findDeep: function(items, attrs) {
var key, n_key, n_value, result, value;
result = _.findWhere(items, attrs);
console.log(items, result, _.isUndefined(result));
if (_.isUndefined(result)) {
for (key in items) {
value = items[key];
for (n_key in value) {
n_value = value[n_key];
if (_.isObject(n_value) || _.isArray(n_value)) {
result = _.findDeep(n_value, attrs);
}
// Once you find the result, you can return the founded result
if (!_.isUndefined(result)) {
return result;
}
}
}
}
return result;
}
});
console.log(_.findDeep(tree, {id: 5456, parent_id: 555}));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

Related

In a javascript nested object, How to get all parents attribute chain by a property like id?(If possible, vanilla javascript)

My object list looks like this:
const a = [
{
id: 1,
name: 'NewYork',
children: [],
},
{
id: 2,
name: 'Tokyo',
children: [
{
id: 7,
name: 'Toshima',
children: [],
},
{
id: 8,
name: 'Minato',
children: [
{
id: 17,
name: 'Sugamo',
children: [],
},
{
id: 18,
name: 'Kamiichi',
children: [],
},
],
},
],
},
];
And now I have an id 18, I'd like to make a String like Tokyo>Minato>Kamiichi
How can I make it work in javascript (no third party library) ?
Actually I have a very long nested object.
One way is to use a recursive function for this:
const a = {
companies: [{
id: 1,
name: 'NewYork',
children: [],
},
{
id: 2,
name: 'Tokyo',
children: [{
id: 7,
name: 'Toshima',
children: [],
},
{
id: 8,
name: 'Minato',
children: [{
id: 17,
name: 'Sugamo',
children: [],
},
{
id: 18,
name: 'Kamiichi',
children: [],
},
],
},
],
},
],
};
function getPath(obj, id) {
if (obj.id === id) return obj.name;
for (const child of obj.children) {
const path = getPath(child, id);
if (path) {
return obj.name ? obj.name + '>' + path : path;
}
}
return null;
}
console.log(getPath({
children: a.companies
}, 18));

A field from nested object to array [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I have the following object, and I want to put all the ids in one array.
let obj = {
_id: 1,
children: [
{
id: 2,
children: [
{
id: 21,
children: [],
},
{
id: 22,
children: [],
},
{
id: 23,
children: [
{
id: 231,
children: [],
}
]
}
]
},
{
id: 3,
children: [
{
id: 31,
children: [],
},
{
id: 32,
children: [],
isCriteria: true
},
{
id: 33,
isCriteria: true
}
]
}
]
}
I want in result:
[1, 2, 21, 22, 23, 231, 3, 31, 32, 33]
PS: the object is set dynamically, so we don't know how deep the children are.
This one is uglier, but also should be lighter on garbage collector because it does not create and spread temporary arrays.
/**
* #param {number[]} arr Target array to which numbers will be appended
* #param {object} obj Source data
* #return {number[]} arr mutated with additional numbers (if any found)
*/
function arrayAppendIdsFrom (arr, obj) {
arr.push(obj.id);
if (Array.isArray(obj.children) && obj.children.length > 0) {
obj.children.forEach(child => arrayAppendIdsFrom(arr, child));
}
return arr;
}
/*
* Example data
*/
let obj = {
id: 1,
children: [
{
id: 2,
children: [
{
id: 21,
children: [],
},
{
id: 22,
children: [],
},
{
id: 23,
children: [
{
id: 231,
children: [],
}
]
}
]
},
{
id: 3,
children: [
{
id: 31,
children: [],
},
{
id: 32,
children: [],
isCriteria: true
},
{
id: 33,
isCriteria: true
}
]
}
]
};
// Example use
console.log(arrayAppendIdsFrom([], obj));
You'll need a recursive iteration. Quick example:
let ids = [];
function getIds(obj) {
// check for id
if (obj.hasOwnProperty('id')) {
ids = [...ids, obj.id];
}
// check for children and do recursive check
if (obj.hasOwnProperty('children') && obj.children.length > 0) {
obj.children.forEach(child => {
getIds(child);
});
}
}
// obj here is your exmple obj with the nested structure
getIds(obj);
console.log(ids);
// [2, 21, 22, 23, 231]
You could use a recursive function like this:
const myObj = obj = {
_id: 1,
children: [
{
id: 2,
children: [
{
id: 21,
children: [],
},
{
id: 22,
children: [],
},
{
id: 23,
children: [
{
id: 231,
children: [],
}
]
}
]
},
{
id: 3,
children: [
{
id: 31,
children: [],
},
{
id: 32,
children: [],
isCriteria: true
},
{
id: 33,
isCriteria: true
}
]
}
]
}
var idArray = []
function func(obj) {
idArray.push(obj._id ? obj._id : obj.id)
if (!obj.children) {
return
}
obj.children.forEach(child => func(child))
}
func(myObj)
console.log(idArray)
You could create a function (fill_ids) which loop every property of obj and push into an array (ids) the values of those with key _id or id, e.g.
let ids = [];
function fill_ids(obj) {
for (i in obj) {
if (i == '_id' || i == 'id') {
ids.push(obj[i]);
} else {
fill_ids(obj[i]);
}
}
}
let obj = {
_id: 1,
children: [{
id: 2,
children: [{
id: 21,
children: [],
}, {
id: 22,
children: [],
}, {
id: 23,
children: [{
id: 231,
children: [],
}]
}]
}, {
id: 3,
children: [{
id: 31,
children: [],
}, {
id: 32,
children: [],
isCriteria: true
}, {
id: 33,
isCriteria: true
}]
}]
};
fill_ids(obj);
console.log(ids);
you may use flatMap
see another example with flatDeep
let obj = {
_id: 1,
children: [
{
id: 2,
children: [
{
id: 21,
children: [],
},
{
id: 22,
children: [],
},
{
id: 23,
children: [
{
id: 231,
children: [],
}
]
}
]
},
{
id: 3,
children: [
{
id: 31,
children: [],
},
{
id: 32,
children: [],
isCriteria: true
},
{
id: 33,
isCriteria: true
}
]
}
]
}
function flatDeep(o) {
return [o.id||o._id].concat( (o.children||[]).flatMap(flatDeep) );
};
console.log(flatDeep(obj));
you could remove the ||o._id and o.children||[] depending on whether your sample input was correct or approximative :)

flatten tree with javascript and es6 features

i am new in es6 and want to flatten my tree object.
(I'm using reflux - not redux - but flatten state is also a good idea in reflux)
api response:
export const node = {
item: 1,
children: [
{
item: 2,
children: [
{
item: 3,
children: [
{
item: 4,
children: []
},
{
item: 5,
children: []
},
{
item: 6,
children: [
{
item: 7,
children: []
},
{
item: 8,
children: []
},
{
item: 9,
children: []
}
]
}
]
},
{
item: 10,
children: [
{
item: 11,
children: []
},
{
item: 12,
children: [
{
item: 13,
children: []
},
{
item: 14,
children: []
}
]
}
]
}
]
}
]
}
My goal is:
tree= {
byId: {
item1 : {
id: 'item1',
name: 'item1', parent: null,
children : ['item2']
}
}
parent is one id, childrend are array of ids
for building a breadcrumb (using parent) or listing child objects...
get object from id with
tree.byId[someId]
my last try ist to use a recursiv function with es6 spread operator:
const flattenTree = (tree, flattenTree) => {
if (node.children.length) {
node.children.map(child => {
return flattenTree(child, [...tree= { id: child.item}])
})
} else {
return [...tree, tree.byId[cat.item] = { id: cat.item, name: cat.item }]
}
}
sorry I'm first time here, so my post in not well formatted...
thx for help
const tree = {
item: 1,
children: [
{
item: 2,
children: [
{
item: 3,
children: [
{
item: 4,
children: []
},
{
item: 5,
children: []
},
{
item: 6,
children: [
{
item: 7,
children: []
},
{
item: 8,
children: []
},
{
item: 9,
children: []
}
]
}
]
},
{
item: 10,
children: [
{
item: 11,
children: []
},
{
item: 12,
children: [
{
item: 13,
children: []
},
{
item: 14,
children: []
}
]
}
]
}
]
}
]
}
const flattenTree = (tree) => {
const newTree = { byId: {} };
const traverseNode = (node, parent) => {
const id = `item${node.item}`;
newTree.byId[id] = {
id,
name: id,
parent,
children: node.children.map((c) => {
traverseNode(c, id)
return `item${c.item.id}`;
})
}
}
traverseNode(tree, null);
return newTree;
}
The result of flattenTree(tree) will look something like this:
{ byId:
{ item4:
{ id: 'item4', name: 'item4', parent: 'item3', children: [] },
item5:
{ id: 'item5', name: 'item5', parent: 'item3', children: [] },
item7:
{ id: 'item7', name: 'item7', parent: 'item6', children: [] },
item8:
{ id: 'item8', name: 'item8', parent: 'item6', children: [] },
item9:
{ id: 'item9', name: 'item9', parent: 'item6', children: [] },
item6:
{ id: 'item6', name: 'item6', parent: 'item3', children: [Array] },
item3:
{ id: 'item3', name: 'item3', parent: 'item2', children: [Array] },
item11:
{ id: 'item11', name: 'item11', parent: 'item10', children: [] },
item13:
{ id: 'item13', name: 'item13', parent: 'item12', children: [] },
item14:
{ id: 'item14', name: 'item14', parent: 'item12', children: [] },
item12:
{ id: 'item12',
name: 'item12',
parent: 'item10',
children: [Array] },
item10:
{ id: 'item10',
name: 'item10',
parent: 'item2',
children: [Array] },
item2:
{ id: 'item2', name: 'item2', parent: 'item1', children: [Array] },
item1:
{ id: 'item1', name: 'item1', parent: null, children: [Array] } } }
You can use this recursive function:
Main() {
let flattedTree = [];
// We must treat the tree like a node, so we put it inside an array.
let tree = [
{
item: 1,
children: [
{
item: 2,
children: [
{
item: 3,
children: [
{
item: 4,
children: [],
},
{
item: 5,
children: [],
},
{
item: 6,
children: [
{
item: 7,
children: [],
},
{
item: 8,
children: [],
},
{
item: 9,
children: [],
},
],
},
],
},
{
item: 10,
children: [
{
item: 11,
children: [],
},
{
item: 12,
children: [
{
item: 13,
children: [],
},
{
item: 14,
children: [],
},
],
},
],
},
],
},
],
},
];
// After recursive method executed you will have a flattend array.
// flattedTree variable hold the flatted tree.
this.flatten(tree, flattedTree);
}
flatten(nodes: any[], flattedNodes: any[]) {
for (let index = 0; index < nodes.length; index++) {
flattedNodes.push(nodes[index]);
if (nodes[index].children !== undefined)
if (nodes[index].children.length > 0)
this.flatten(nodes[index].children, flattedNodes);
}
}
This is the result:
[{"item":1,"children":[{"item":2,"children":[{"item":3,"children":[{"item":4,"children":[]},{"item":5,"children":[]},{"item":6,"children":[{"item":7,"children":[]},{"item":8,"children":[]},{"item":9,"children":[]}]}]},{"item":10,"children":[{"item":11,"children":[]},{"item":12,"children":[{"item":13,"children":[]},{"item":14,"children":[]}]}]}]}]},{"item":2,"children":[{"item":3,"children":[{"item":4,"children":[]},{"item":5,"children":[]},{"item":6,"children":[{"item":7,"children":[]},{"item":8,"children":[]},{"item":9,"children":[]}]}]},{"item":10,"children":[{"item":11,"children":[]},{"item":12,"children":[{"item":13,"children":[]},{"item":14,"children":[]}]}]}]},{"item":3,"children":[{"item":4,"children":[]},{"item":5,"children":[]},{"item":6,"children":[{"item":7,"children":[]},{"item":8,"children":[]},{"item":9,"children":[]}]}]},{"item":4,"children":[]},{"item":5,"children":[]},{"item":6,"children":[{"item":7,"children":[]},{"item":8,"children":[]},{"item":9,"children":[]}]},{"item":7,"children":[]},{"item":8,"children":[]},{"item":9,"children":[]},{"item":10,"children":[{"item":11,"children":[]},{"item":12,"children":[{"item":13,"children":[]},{"item":14,"children":[]}]}]},{"item":11,"children":[]},{"item":12,"children":[{"item":13,"children":[]},{"item":14,"children":[]}]},{"item":13,"children":[]},{"item":14,"children":[]}]

Javascript find node from tree with recursive function

I have JavaScript tree data like this.
const tree = {
children:[
{id: 10, children: [{id: 34, children:[]}, {id: 35, children:[]}, {id: 36, children:[]}]},
{id: 10,
children: [
{id: 34, children:[
{id: 345, children:[]}
]},
{id: 35, children:[]},
{id: 36, children:[]}
]
},
{id: 11, children: [{id: 30, children:[]}, {id: 33, children:[]}, {id: 3109, children:[]}]}
],
id: 45
}
const getByID = (tree, id) => {
let result = null
if (id === tree.id) {
return tree
} else {
if(tree.children){
tree.children.forEach( node=> {
result = getByID(node, id)
})
}
return result
}
}
const find345 = getByID(tree, 345)
console.log(find345)
I was try to find item by its id from this tree. im using recursive function to iterate the tree and its children, but it wont find the item as i expected.
its always return null. expected to return {id: 345, children:[]}
You need to exit the loop by using a method which allows a short circuit on find.
The problem with visiting nodes but already found the node, is the replacement of the result with a later wrong result. You need to exit early with a found node.
Array#some allows to iterate and to exit the loop if a truty value is returned. In this case the result is truthy on find.
const tree = { children: [{ id: 10, children: [{ id: 34, children: [] }, { id: 35, children: [] }, { id: 36, children: [] }] }, { id: 10, children: [{ id: 34, children: [{ id: 345, children: [] }] }, { id: 35, children: [] }, { id: 36, children: [] }] }, { id: 11, children: [{ id: 30, children: [] }, { id: 33, children: [] }, { id: 3109, children: [] }] }], id: 45 };
const getByID = (tree, id) => {
let result = null
if (id === tree.id) {
return tree
} else {
if(tree.children){
tree.children.some(node => result = getByID(node, id));
// ^^^^ exit if found
// ^^^^^^^^^^^^^^^^^^^^^^^^^^ return & assign
}
return result;
}
}
const find345 = getByID(tree, 345)
console.log(find345)
A bit shorter
var tree = { children: [{ id: 10, children: [{ id: 34, children: [] }, { id: 35, children: [] }, { id: 36, children: [] }] }, { id: 10, children: [{ id: 34, children: [{ id: 345, children: [] }] }, { id: 35, children: [] }, { id: 36, children: [] }] }, { id: 11, children: [{ id: 30, children: [] }, { id: 33, children: [] }, { id: 3109, children: [] }] }], id: 45 },
getByID = (tree, id) => {
var temp;
return tree.id === id
? tree
: (tree.children || []).some(o => temp = getByID(o, id)) && temp;
};
console.log(getByID(tree, 345));
We can also use reduce method for recursive function
function findAllByKey(obj, keyToFind) {
return Object.entries(obj)
.reduce((acc, [key, value]) => (key === keyToFind)
? acc.concat(value)
: (typeof value === 'object' && value)
? acc.concat(findAllByKey(value, keyToFind))
: acc
, []) || [];
}

Build Object Recursive

I have a Object which looks like the following obj.
var obj = [
{ id: 1, name: "animals" },
{ id: 2, name: "animals_cat" },
{ id: 3, name: "animals_dog" },
{ id: 4, name: "animals_weazle" },
{ id: 5, name: "animals_weazle_sand shadow weazle" },
{ id: 11, name: "fruits" },
{ id: 32, name: "fruits_banana" },
{ id: 10, name: "threes" },
{ id: 15, name: "cars" }
];
The Object should be converted into the following scheme:
var items = [
{ id: 11, name: "fruits", items: [
{ id: 32, name: "banana" }
]},
{ id: 10, name: "threes" },
{ id: 1, name: "animals", items: [
{ id: 2, name: "cat" },
{ id: 3, name: "dog" },
{ id: 4, name: "weazle", items: [
{ id: 5, name: "sand shadow weazle" }
]}
]},
{ id: 15, name: "cars" }
];
I tried a lot but unfortunately without any success. I did $.each on obj, did a split('_') on it and pushed it to items. But how can I do it for unlimited depth and push it into the right category?
I'm happy for any help.
Maybe this helps.
It works with Array.prototype.forEach for processing obj, Array.prototype.reduce for getting the right branch and Array.prototype.some for the right array element for inserting the new object.
This proposal works for sorted and consistent data.
var obj = [
{ id: 1, name: "animals" },
{ id: 2, name: "animals_cat" },
{ id: 3, name: "animals_dog" },
{ id: 4, name: "animals_weazle" },
{ id: 5, name: "animals_weazle_sand shadow weazle" },
{ id: 11, name: "fruits" },
{ id: 32, name: "fruits_banana" },
{ id: 10, name: "threes" },
{ id: 15, name: "cars" }
],
tree = [];
obj.forEach(function (a) {
var path = a.name.split('_'),
o = {};
o.id = a.id;
path.reduce(function (r, b) {
o.name = b;
r.some(function (c) {
if (c.name === b) {
c.items = c.items || [];
r = c.items;
return true;
}
});
return r;
}, tree).push(o);
});
document.write('<pre>' + JSON.stringify(tree, 0, 4) + '</pre>');
Update: Version for independent order of items.
var obj = [
{ id: 5, name: "animals_weazle_sand shadow weazle" },
{ id: 32, name: "fruits_banana" },
{ id: 1, name: "animals" },
{ id: 2, name: "animals_cat" },
{ id: 3, name: "animals_dog" },
{ id: 4, name: "animals_weazle" },
{ id: 11, name: "fruits" },
{ id: 10, name: "threes" },
{ id: 15, name: "cars" },
{ id: 999, name: "music_pop_disco_euro"}
],
tree = [];
obj.forEach(function (item) {
var path = item.name.split('_'),
o = tree;
path.forEach(function (a, i) {
var oo = { name: a, items: [] },
last = path.length - 1 === i,
found = o.some(function (b) {
if (b.name === a) {
if (last) {
b.id = item.id;
return true;
}
b.items = b.items || [];
o = b.items;
return true;
}
});
if (!found) {
if (last) {
o.push({ id: item.id, name: a });
} else {
o.push(oo);
o = oo.items;
}
}
});
});
document.write('<pre>' + JSON.stringify(tree, 0, 4) + '</pre>');

Categories