Related
I have data that is structured like the following:
const arr = [{
id: 0,
name: 'Biomes',
icon: 'mdi-image-filter-hdr',
isParent: true,
children: [{
id: 1,
name: 'Redwood forest',
icon: 'mdi-image-filter-hdr'
}]
},{
id: 2,
name: 'Trees',
icon: 'mdi-pine-tree',
children: [{
id: 8,
name: 'Redwood',
icon: 'mdi-pine-tree'
}]
}];
The top level is an array of objects, and each object can have a child field children which itself can be an array of objects. If I know the value of id, how can I find the object (or more importantly the object name), based on that id value?
const arr = [{
id: 0,
name: 'Biomes',
icon: 'mdi-image-filter-hdr',
isParent: true,
children: [{
id: 1,
name: 'Redwood forest',
icon: 'mdi-image-filter-hdr'
}]
},{
id: 2,
name: 'Trees',
icon: 'mdi-pine-tree',
children: [{
id: 0,
name: 'whatever',
icon: 'new-tree'
},{
id: 8,
name: 'Redwood',
icon: 'mdi-pine-tree'
}]
}];
const findById = (id) => arr
.filter(x => x.id === id || x.children.some(child => id === child.id))
.map(y => ({...y, children: y.children.filter(child => id === child.id)}))
console.log(findById(0))
You can first filter out all parents who has the same id or if the parent has child with same id via some, which then using map to and filter to remove all children who don't have the id
You can use recursion here to get the object no matter how deep it is. I've filtered out the object. It will only give the object without children property. You can include it in the way you want
function getObjectFromId(arr, id) {
for (let i = 0; i < arr.length; ++i) {
if (arr[i].id === id) {
// return arr[i] // If you want full object with children
const { children, ...rest } = arr[i];
return rest;
}
if (arr[i].children) {
const result = getObjectFromId(arr[i].children, id);
if (result) return result;
}
}
}
const arr = [
{
id: 0,
name: "Biomes",
icon: "mdi-image-filter-hdr",
isParent: true,
children: [
{
id: 1,
name: "Redwood forest",
icon: "mdi-image-filter-hdr",
},
],
},
{
id: 2,
name: "Trees",
icon: "mdi-pine-tree",
children: [
{
id: 8,
name: "Redwood",
icon: "mdi-pine-tree",
children: [
{
id: 9,
name: "Redwood",
icon: "mdi-pine-tree",
},
],
},
],
},
];
function getObjectFromId(arr, id) {
for (let i = 0; i < arr.length; ++i) {
if (arr[i].id === id) {
// return arr[i] // If you want full object with children
const { children, ...rest } = arr[i];
return rest;
}
if (arr[i].children) {
const result = getObjectFromId(arr[i].children, id);
if (result) return result;
}
}
}
console.log(getObjectFromId(arr, 1));
console.log(getObjectFromId(arr, 8));
console.log(getObjectFromId(arr, 9));
/* This is not a part of answer. It is just to give the output fill height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
Solution 1: 2 for loop.
const inputArray = [{
id: 0,
name: 'Biomes',
icon: 'mdi-image-filter-hdr',
isParent: true,
children: [{
id: 1,
name: "Redwood forest",
icon: 'mdi-image-filter-hdr'
}]
},
{
id: 2,
name: 'Trees',
icon: 'mdi-pine-tree',
children: [{
id: 8,
name: "Redwood",
icon: 'mdi-pine-tree',
}]
}
];
console.log(findById(8));
function findById(id) {
for (const parent of inputArray) {
if (parent.id === id)
return parent;
for (const child of parent.children)
if (child.id === id)
return child;
}
return null;
}
Solution 2: multiple nested level:
const inputArray = [{
id: 0,
name: 'Biomes',
icon: 'mdi-image-filter-hdr',
isParent: true,
children: [{
id: 1,
name: "Redwood forest",
icon: 'mdi-image-filter-hdr'
}]
},
{
id: 2,
name: 'Trees',
icon: 'mdi-pine-tree',
children: [{
id: 8,
name: "Redwood",
icon: 'mdi-pine-tree',
children: [{
id: 12,
name: "Test Level 2",
icon: 'mdi-pine-tree',
children: [{
id: 13,
name: "Test Level 3",
icon: 'mdi-pine-tree',
}]
}]
}]
}
];
console.log(findById(13, inputArray));
function findById(id, array) {
for (const parent of array) {
if (parent.id === id)
return parent;
if ('children' in parent) {
const result = findById(id, parent.children);
if (result) return result;
}
}
return null;
}
To find at parent and child level
const arr=[{id:0,name:'Biomes',icon:'mdi-image-filter-hdr',isParent:!0,children:[{id:1,name:"Redwood forest",icon:'mdi-image-filter-hdr'}]},{id:2,name:'Trees',icon:'mdi-pine-tree',children:[{id:8,name:"Redwood",icon:'mdi-pine-tree',}]}]
const res = arr.reduce((a,item)=>{
const { children, ...i} = item;
a.push(...children, i);
return a;
},[]);
function findInData(id){
return res.find(o=>o.id===id);
}
console.log(findInData(8)?.name);
console.log(findInData(2)?.name);
First arg for child
Second arg for main
const arr = [
{id: 0,name: 'Biomes',icon: 'mdi-image-filter-hdr',isParent: true,
children: [{id: 1,name: 'Redwood forest',icon: 'mdi-image-filter-hdr',},],},
{id: 2,name: 'Trees',icon: 'mdi-pine-tree',
children: [{id: 8,name: 'Redwood',icon: 'mdi-pine-tree',},],},
];
const getChildById = (_id, _id2 = false)=>{
for(const main of arr){
if(_id2 !== false && main.id !== _id2)continue;
for(const child of main.children){
if(child.id === _id)return child;
}
}
return false;
};
(async()=>{
const i_want_this_id = 8;
//
const a = getChildById(i_want_this_id);
console.log('Found', a);
})();
function findChildrenById(data, cid) {
for (let p = 0; p < data.length; p += 1) {
if (data[p].id === cid) return data[p].name
let res = data[p].children.find(c => c.id === cid)
if (res) return res.name
}
return null
}
const arr = [{
id: 0,
name: 'Biomes',
icon: 'mdi-image-filter-hdr',
isParent: true,
children: [{
id: 1,
name: 'Redwood forest',
icon: 'mdi-image-filter-hdr'
}]
}, {
id: 2,
name: 'Trees',
icon: 'mdi-pine-tree',
children: [{
id: 8,
name: 'Redwood',
icon: 'mdi-pine-tree'
}]
}];
console.log(findChildrenById(arr, 2))
One way is by using Array find() method
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
let object= arr.find((item)=>item.id === "your ID");
console.log(object?.name);
const arr = [{
id: 0,
name: 'Biomes',
icon: 'mdi-image-filter-hdr',
isParent: true,
children: [{
id: 1,
name: 'Redwood forest',
icon: 'mdi-image-filter-hdr'
}]
},{
id: 2,
name: 'Trees',
icon: 'mdi-pine-tree',
children: [{
id: 8,
name: 'Redwood',
icon: 'mdi-pine-tree'
}]
}];
let object= arr.find((item)=>item.id === 2);
console.log(object?.name);
let object= arr.find((item)=>item.id === "your ID");
console.log(object?.name);
const Data = [
{
id: 1,
name: "jack"
},
{
id: 1,
name: "george"
},
{
id: 1,
name: "morries"
},
{
id: 1,
name: "kok"
},
{
id: 2,
name: "abdo"
},
{
id: 2,
name: "ezz"
},
{
id: 3,
name: "mostafa"
},
{
id: 3,
name: "tox"
},
{
id: 4,
name: "hamo"
}]
var new_array = [];
for (var i = 0; i < Data.length; i++) {
var data_id = Data[i].id;
var data_name = Data[i];
var ref_index = add(new_array, data_id);
new_array[ref_index].names.push({ data: data_name });
}
console.log(new_array);
function add(data_array, id_value) {
var index = data_array.findIndex(x => x.id === id_value);
if (index === -1) {
data_array.push({ id: id_value, names: [] });
index = data_array.findIndex(x => x.id === id_value);
}
return index;
}
why my response like this I need to see the value of my objects and I don't know what am missing!
[
{ id: 1, names: [[Object], [Object], [Object], [Object]] },
{ id: 2, names: [[Object], [Object]] },
{ id: 3, names: [[Object], [Object]] },
{ id: 4, names: [[Object]] }
]
I need to see the object itself so I try the code but it's response like the above of the code?
try:
1- The Spreed Operator.
var data_name = {...Data[i]};
2- Stringify The Object.
var data_name = JSON.stringify(Data[i]);
3- The "assign" Method.
var data_name = Object.assign({}, Data[i]);
Given the following JSON Array:
[{"ID":12,"NAME":"ktc","PARENTID":0},
{"ID":11,"NAME":"root","PARENTID":0},
{"ID":1,"NAME":"rwhitney","PARENTID":0},
{"ID":21,"NAME":"shared folder","PARENTID":0},
{"ID":2,"NAME":".config","PARENTID":1},
{"ID":5,"NAME":"wallpapers","PARENTID":1},
{"ID":3,"NAME":"geany","PARENTID":2},
{"ID":4,"NAME":"colorschemes","PARENTID":3},
{"ID":13,"NAME":"efast","PARENTID":12},
{"ID":15,"NAME":"includes","PARENTID":13},
{"ID":14,"NAME":"views","PARENTID":13},
{"ID":17,"NAME":"css","PARENTID":15},
{"ID":16,"NAME":"js","PARENTID":15}]
I need to build a menu tree with the subfolders nested beneath the parent folders.
Here is some server side code:
socket.on('get-folders', function(data){
var folders = [];
getSession(session.key, function(currSession){
db.rows('getFolders', currSession, [currSession.user], function(err, rows){
if (err) {
socket.emit('err', 'Error is: ' + err );
} else if(rows[0]){
//~ folders.push(JSON.stringify(rows));
socket.emit('get-folders', JSON.stringify(rows));
//~ n_Folders(rows, currSession, socket, folders, 0);
}
});
});
});
and client side:
function rtnSocket(cmd, data, cb){
socket.emit(cmd, data);
socket.on(cmd, cb);
}
rtnSocket('get-folders', folderid, function(data){
console.log(data);
});
can someone please help guide me in the right direction?
You could collect all nodes from a flat data structure, use the ID and PARENTID as keys in a hash table and get the root array as result.
var data = [{ ID: 12, NAME: "ktc", PARENTID: 0 }, { ID: 11, NAME: "root", PARENTID: 0 }, { ID: 1, NAME: "rwhitney", PARENTID: 0 }, { ID: 21, NAME: "shared folder", PARENTID: 0 }, { ID: 13, NAME: "efast", PARENTID: 12 }, { ID: 2, NAME: ".config", PARENTID: 1 }, { ID: 5, NAME: "wallpapers", PARENTID: 1 }, { ID: 15, NAME: "includes", PARENTID: 13 }, { ID: 14, NAME: "views", PARENTID: 13 }, { ID: 3, NAME: "geany", PARENTID: 2 }, { ID: 17, NAME: "css", PARENTID: 15 }, { ID: 16, NAME: "js", PARENTID: 15 }, { ID: 4, NAME: "colorschemes", PARENTID: 3 }],
tree = function (data, root) {
var t = {};
data.forEach(o => {
Object.assign(t[o.ID] = t[o.ID] || {}, o);
t[o.PARENTID] = t[o.PARENTID] || {};
t[o.PARENTID].children = t[o.PARENTID].children || [];
t[o.PARENTID].children.push(t[o.ID]);
});
return t[root].children;
}(data, 0);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Its better to put the subFolders in inside the Parent Object as SubFolder Array .. it will eliminate ParentID and make easy to traverse
[
{
"ID": 1,
"NAME": "rwhitney",
"PARENTID": 0,
"SUB": [
{
"ID": 2,
"NAME": ".config",
"PARENTID": 1,
"SUB": [
{
"ID": 3,
"NAME": "geany",
"PARENTID": 2,
"SUB": [
{
"ID": 4,
"NAME": "colorschemes",
"PARENTID": 3
}
]
}
]
},
{
"ID": 5,
"NAME": "wallpapers",
"PARENTID": 1
}
]
}
]
At first we need to flat nested array:
const flatArray = (arr) => {
return arr.reduce((flat, toFlatten) => {
return flat.concat(Array.isArray(toFlatten) ? flatArray(toFlatten) : toFlatten);
}, []);
}
Then we can create a tree:
const makeTree = dataset => {
let hashTable = Object.create(null)
dataset.forEach( aData => hashTable[aData.ID] = { ...aData, childNodes : [] } )
let dataTree = []
dataset.forEach( aData => {
if( aData.PARENTID ) hashTable[aData.PARENTID].childNodes.push(hashTable[aData.ID])
else dataTree.push(hashTable[aData.ID])
} )
return dataTree
}
An example:
let data = [
[{ ID: 12, NAME: "ktc", PARENTID: 0 }, { ID: 11, NAME: "root", PARENTID: 0 }, { ID: 1, NAME: "rwhitney", PARENTID: 0 },
{ ID: 21, NAME: "shared folder", PARENTID: 0 }], [{ ID: 13, NAME: "efast", PARENTID: 12 }], [{ ID: 2, NAME: ".config", PARENTID: 1 },
{ ID: 5, NAME: "wallpapers", PARENTID: 1 }], [{ ID: 15, NAME: "includes", PARENTID: 13 }, { ID: 14, NAME: "views", PARENTID: 13 }],
[{ ID: 3, NAME: "geany", PARENTID: 2 }], [{ ID: 17, NAME: "css", PARENTID: 15 }, { ID: 16, NAME: "js", PARENTID: 15 }],
[{ ID: 4, NAME: "colorschemes", PARENTID: 3 }]];
const flatArray = (arr) => {
return arr.reduce((flat, toFlatten) => {
return flat.concat(Array.isArray(toFlatten) ? flatArray(toFlatten) : toFlatten);
}, []);
}
const makeTree = dataset => {
let hashTable = Object.create(null)
dataset.forEach( aData => hashTable[aData.ID] = { ...aData, childNodes : [] } )
let dataTree = []
dataset.forEach( aData => {
if( aData.PARENTID ) hashTable[aData.PARENTID].childNodes.push(hashTable[aData.ID])
else dataTree.push(hashTable[aData.ID])
} )
return dataTree
}
const dataTree = makeTree(flatArray(data));
console.log(dataTree)
I need to answer my own question with the help of Nina above:
With the given JSON object - answer from Nina:
[{"ID":12,"NAME":"ktc","PARENTID":0},{"ID":11,"NAME":"root","PARENTID":0},{"ID":1,"NAME":"rwhitney","PARENTID":0},{"ID":21,"NAME":"shared folder","PARENTID":0},{"ID":2,"NAME":".config","PARENTID":1},{"ID":5,"NAME":"wallpapers","PARENTID":1},{"ID":3,"NAME":"geany","PARENTID":2},{"ID":4,"NAME":"colorschemes","PARENTID":3},{"ID":13,"NAME":"efast","PARENTID":12},{"ID":15,"NAME":"includes","PARENTID":13},{"ID":14,"NAME":"views","PARENTID":13},{"ID":17,"NAME":"css","PARENTID":15},{"ID":16,"NAME":"js","PARENTID":15},{"ID":27,"NAME":"images","PARENTID":16}]
I came up with this function:
var LHR= '',folderid = 0, parentid = 0;
var seg = location.pathname.split('/')[2];
if(seg){
LHR = seg.split('_')[0];
folderid = seg.split('_')[1] || 0;
//~ alert(folderid);
parentid = seg.split('_')[2] || 0;
if(isLike(LHR,['share']) == true){
sharedFileID = LHR.split('-')[1];
}
}
LHR = LHR.replace(/%20/g,' ');
var MLHR = isLike(LHR, ['share']) == true ? LHR.split('-')[0] : LHR;
var folders = '';
function recurse(data, indent, limit){
if(limit < 10){
for(var i = 0;i<data.length;i++){
if(folderid == data[i].ID){
folders += '<div><input style="margin-left:60px" type="checkbox" data-id="'+folderid+'" data-name="' + data[i].NAME + '" class="check-folder tooltip">'+
'<img style="margin-left:0px" data-pid="'+parentid+'" id="folder-'+folderid+'" src="/fa/folder-open.svg" class="blk tooltip"> ' + MLHR.replace(/%20/g,' ') + ' </div>';
} else {
folders += '<input type="checkbox" style="margin-left:'+indent+'px" data-id="'+data[i].ID+'" data-name="' + data[i].NAME + '" class="check-folder tooltip">'+
'<a style="margin-left:70px" ondrop="drop(event)" ondragover="allowDrop(event)" class="dsp-ib w150 blk folders drop drag" draggable="true" droppable="true" href="/dashboard/'+data[i].NAME+'_'+data[i].ID+'_'+data[i].PARENTID+'">'+
'<img data-pid="'+data[i].PARENTID+'" src="/fa/folder.svg" class="fa ml--80 blk dsp-ib" id="folder-'+data[i].ID+'"> ' + data[i].NAME + '</a><br>';
}
if(data[i].children){
recurse(data[i].children, indent+=20, ++limit);
}
}
}
$('#folders').html(folders);
}
and call it like thus:
recurse(data,0,0);
The function populates the tree of id "folders"
with the following output:
Thanks again for setting me on the right track!
I want to retrieve all child ids of a specific group, which can be deeply nested or not.
Here is a sample json:
[
{
id: 1,
name: 'Desjardins Group 1',
children: [
{ id: 2, name: 'Analysts', children: [] },
{ id: 3, name: 'Administration', children: [] }
]
},
{
id: 4,
name: 'Desjardins Group 2',
children: [
{ id: 5, name: 'Consultants1', children: [] },
{
id: 6,
name: 'Consultant2',
children: [
{
id: 7, name: 'Interns', children: [
{ id: 8, name: 'subInterns1', children: [] },
{ id: 9, name: 'subInterns2', children: [] },
{ id: 10, name: 'subInterns3', children: [] }
]
}
]
}
]
}
]
I'm trying to make a function that takes an id has a parameter, and return all child ids.
Ex: getChildGroups(6) would return 7, 8, 9 and 10.
I guess recursive function and filters are the way to go, but i can't find a proper example.
Here's a simplified version of Johann Bauer's answer.
The first function just finds the first node that matches the given ID, with no need for any accumulation of data:
function findNode(data, id) {
if (!Array.isArray(data)) return;
for (let entry of data) {
if (entry.id === id) {
return entry;
} else {
const node = findNode(entry.children, id);
if (node) {
return node;
}
}
}
}
This second function just gets the child IDs, storing them in the passed array, without any intermediate arrays being created:
function getChildIds(node, result = []) {
if (!node) return;
if (!Array.isArray(node.children)) return;
for (let entry of node.children) {
result.push(entry.id);
getChildIds(entry, result);
}
return result;
}
It might be a good idea to split your problem into two smaller problems:
Find a group of ID x somewhere nested in the graph
Given a node, return all their sub-node IDs recursively
The solution to the first problem could look something like this:
function findGroupId(o, id) {
if (o.id == id) {
// We found it!
return o;
}
if (Array.isArray(o)) {
// If we start with a list of objects, pretend it is the root node
o = {children: o}
}
let results = [];
for (let c of o.children) {
// recursively call this function again
results.push(findGroupId(c, id))
}
// return the first matching node
return results.filter(r => r !== undefined)[0];
}
And for the second problem:
function getAllChildrenIDs(o) {
if (o.children === undefined)
return [];
let ids = [];
for (c of o.children) {
ids.push(c.id);
// recursively call this function again
for (id of getAllChildrenIDs(c))
ids.push(id);
}
return ids;
}
And if we put this together:
let example = [{
id: 1,
name: 'Desjardins Group 1',
children: [{
id: 2,
name: 'Analysts',
children: []
},
{
id: 3,
name: 'Administration',
children: []
}
]
},
{
id: 4,
name: 'Desjardins Group 2',
children: [{
id: 5,
name: 'Consultants1',
children: []
},
{
id: 6,
name: 'Consultant2',
children: [{
id: 7,
name: 'Interns',
children: [{
id: 8,
name: 'subInterns1',
children: []
},
{
id: 9,
name: 'subInterns2',
children: []
},
{
id: 10,
name: 'subInterns3',
children: []
}
]
}]
}
]
}
];
function findGroupId(o, id) {
if (o.id == id) {
return o;
}
if (Array.isArray(o)) {
o = {
children: o
}
}
let results = [];
for (let c of o.children) {
results.push(findGroupId(c, id))
}
return results.filter(r => r !== undefined)[0];
}
function getAllChildrenIDs(o) {
if (o.children === undefined)
return [];
let ids = [];
for (c of o.children) {
ids.push(c.id);
for (id of getAllChildrenIDs(c))
ids.push(id);
}
return ids;
}
console.log(getAllChildrenIDs(findGroupId(example, 6)))
I have a specific case and I don't even know if it is possible to achieve.
Given the input array.
var originalArr = [
[
{ ID: 3, name: 'Beef' },
{ ID: 4, name: 'Macaroni' },
{ ID: 5, name: 'Sauce#1' }
],
[{ ID: 1, name: 'Lettuce' }, { ID: 2, name: 'Brocoli' }]
];
I would like to iterate over the inner arrays and pick the ID's from objects then create new one in place of array. So my output should look something like this.
var output = [
{
'1': {
name: 'Lettuce',
ID: 1
},
'2': {
name: 'Brocoli',
ID: 2
}
},
{
'3': {
name: 'Beef',
ID: 3
},
'4': {
name: 'Macaroni',
ID: 4
},
'5': {
name: 'Sauce#1'
}
}
];
It is easy to iterate over the inner arrays with map but how can I create new Object in place of the array and have its key value dynamically pulled up ? And is it even possible given my input to produce the desired output.
Use map and reduce
originalArr.map( s => //iterate outer array
s.reduce( (acc, c) => ( //iterate inner array using reduce
acc[c.ID] = c, acc //assign the id as key to accumulator and return the accumulator
) , {}) //initialize accumulator to {}
)
Demo
var originalArr = [
[
{ ID: 3, name: 'Beef' },
{ ID: 4, name: 'Macaroni' },
{ ID: 5, name: 'Sauce#1' }
],
[{ ID: 1, name: 'Lettuce' }, { ID: 2, name: 'Brocoli' }]
];
var output = originalArr.map( s => s.reduce( (acc, c) => ( acc[c.ID] = c, acc ) , {}) );
console.log(output);
You can achieve using recursion with pure javascript
var originalArr = [
[{
ID: 3,
name: 'Beef'
}, {
ID: 4,
name: 'Macaroni'
}, {
ID: 5,
name: 'Sauce#1'
}],
[{
ID: 1,
name: 'Lettuce'
}, {
ID: 2,
name: 'Brocoli'
}]
]
function bindInObject(object, array) {
for (var i = 0; i < array.length; i++) {
var it = array[i];
if (it instanceof Array) {
bindInObject(object, it);
} else {
var id = it.ID;
object[id] = it;
}
}
}
var output = {};
bindInObject(output, originalArr);
console.log(output)
const original_array = [
[
{ ID: 3, name: 'Beef' },
{ ID: 4, name: 'Macaroni' },
{ ID: 5, name: 'Sauce#1' }
],
[
{ ID: 1, name: 'Lettuce' },
{ ID: 2, name: 'Brocoli' }
]
]
let new_array = []
for (let i=0; i < original_array.length; i++) {
if (original_array[i + 1]) new_array =
new_array.concat(original_array[i].concat(original_array[i+1]))
}
let output = []
for (let i=0; i<new_array.length; i++) {
output.push({[new_array[i].ID]: new_array[i]})
}