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 :)
Related
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));
I have JS object like this one:
const networkTree = [
{
id: 10,
hasChildren: true,
children: [
{
id: 9,
hasChildren: true,
children: [
{
id: 7,
hasChildren: true,
children: [
{
id: 5,
hasChildren: false,
children: [],
},
{
id: 4,
hasChildren: false,
children: [],
},
],
},
{
id: 6,
hasChildren: true,
children: [
{
id: 3,
hasChildren: false,
children: [],
},
{
id: 2,
hasChildren: false,
children: [],
},
],
},
],
},
{
id: 8,
hasChildren: true,
children: [
{
id: 1,
hasChildren: false,
children: [],
},
{
id: 11,
hasChildren: false,
children: [],
},
],
},
],
},
];
I have to convert it with a function takes input js object like above to this order:
const myTreeData = [
{
name: 'Top Level',
children: [
{
name: 'Level 2: A',
},
{
name: 'Level 2: B',
},
],
},
];
I am stucked at this point:
const convertTree = (array) => (
treeDatas[];
treeData[];
for(i = 0 ; i < array.length ; i++) {
treeDatas.push(array[i]);
treeData[i].name.push(array[i].id);
}
);
If input networkTree[0].id is 15, myTreeData[0].name should be 15. I want to use id's as names on converted object. I don't need hasChildren boolean value. I will use converted object format with react-ds3-component. If you need more information about converted object format please check https://www.npmjs.com/package/react-d3-tree
If I understand you correctly you want to rename id to name and remove hasChildren? Then you can use this recursive function.
const convertTree = (tree) =>
tree.map((node) => ({
name: node.id,
children: convertTree(node.children)
}))
It's the same as this function, just less verbal.
const convertTree = (tree) => {
return tree.map((node) => {
return {
name: node.id,
children: convertTree(node.children)
}
})
}
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":[]}]
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
, []) || [];
}
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>