Trying to generate dropdown with deep nested elements.
Incoming data:
111: {id: 111, name: '111' },
222: {id: 222, name: '222' },
333: {id: 333, name: '333', parent: {id: 222} },
444: {id: 444, name: '444', parent: {id: 333} },
555: {id: 555, name: '555' }
I know only parent and I want to generate a tree for React template.
It's going to be like this:
result:
[{
id: 111,
name: '111'
},
{
id: 222,
name: '222',
children: [{
id: 333,
name: '333',
parent: {
id: 222
},
children: [{
id: 444,
name: '444',
parent: {
id: 333
}
}]
}
]
},
{
id: 555,
name: '555'
}
]
You could take temporary object for keeping all references to the same id and build a tree with the parts.
This works for unsorted data as well.
var data = { 111: { id: 111, name: '111' }, 222: { id: 222, name: '222' }, 333: { id: 333, name: '333', parent: { id: 222 } }, 444: { id: 444, name: '444', parent: { id: 333 } }, 555: { id: 555, name: '555' } },
tree = function (object, root) {
var r = [], o = {};
Object.keys(object).forEach(function (k) {
var id = object[k].id;
o[id] = Object.assign(o[id] || {}, object[k]);
if (o[id].parent === root) {
r.push(o[id]);
} else {
o[o[id].parent.id] = o[o[id].parent.id] || {};
o[o[id].parent.id].children = o[o[id].parent.id].children || [];
o[o[id].parent.id].children.push(o[id]);
}
});
return r;
}(data, undefined);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I take a time for make a demo, but look at your object is worg no passed any json validator.
var _data = [{
id: '111',
name: '111'
}, {
id: '222',
name: '222',
children: [
{
id: '333',
name: '333',
parent: {
id: '222'
},
children: [
{
id: '444',
name: '444',
parent: {
id: '333'
}
}
]
}]
}
];
console.log(_data);
function make(arr){
var _arr = [];
function _do(arr, _parent){
for(var i=0; i<arr.length;i++){
var _o = {
id: arr[i].id,
name: arr[i].name
};
if(_parent){
_o.parent = _parent;
}
if(arr[i].children){
_do(arr[i].children, arr[i].id);
}
_arr[arr[i].id] = _o;
}
}
_do(arr);
return _arr
};
console.log(make(_data));
You can try following. You can solve n level nesting with it.
var obj = {
111: {id: 111, name: '111' },
222: {id: 222, name: '222' },
333: {id: 333, name: '333', parent: {id: 222} },
444: {id: 444, name: '444', parent: {id: 333} },
555: {id: 555, name: '555' }
};
// Iterate over the object keys and create the tree and only push items which have no parent in response
var response = [];
Object.keys(obj).forEach(function(key) {
var item = obj[key];
if (item.parent) {
obj[item.parent.id].children = obj[item.parent.id].children || [];
obj[item.parent.id].children.push(obj[key]);
} else {
response.push(obj[key]);
}
});
console.log(response);
Related
There is one scenario where i need to replace the existing records from cached data with new incoming data source. Looking for the cleaner approach to handle the array operations.
For example:
var userCategory = [
{
id: 'platinum',
name: 'bob',
},
{
id: 'platinum',
name: 'bar',
},
{
id: 'platinum',
name: 'foo',
},
{
id: 'gold',
name: 'tom',
},
{
id: 'silver',
name: 'billy',
},
];
Here is new users of particular category
var newPlatinumUsers = [
{
id: 'platinum',
name: 'bob',
},
{
id: 'platinum',
name: 'mike',
},
];
This is the expected result needed:
var expected = [
{
id: 'platinum',
name: 'bob',
},
{
id: 'platinum',
name: 'mike',
},
{
id: 'gold',
name: 'tom',
},
{
id: 'silver',
name: 'billy',
},
];
I tried with filtering all the platinum user from existing records then added the new records but it looks verbose
Is there any cleaner approach like lodash operator??
Thanks for your time!!!
May you are looking for this.
function getUnique(arr){
// removing duplicate
let uniqueArr = [...new Set(arr)];
document.write(uniqueArr);
}
const array = ['acer','HP','Apple','Apple','something'];
// calling the function
getUnique(array);
Verify my answer if it help you.
Please find the Javascript implementation of the same
var userCategory = [
{ id: 'platinum', name: 'bob', },
{ id: 'platinum', name: 'bar', },
{ id: 'platinum', name: 'foo', },
{ id: 'gold', name: 'tom', },
{ id: 'silver', name: 'billy', },
];
var newPlatinumUsers = [
{ id: 'platinum', name: 'bob', },
{ id: 'platinum', name: 'mike', },
];
const result = [...newPlatinumUsers];
userCategory.forEach((node) => {
if(node.id !== 'platinum') {
result.push(node);
}
});
console.log(result);
With this solution you can change more than one category:
var userCategory = [
{id: 'platinum',name: 'bob'},
{id: 'platinum',name: 'bar'},
{id: 'platinum',name: 'foo'},
{id: 'gold',name: 'tom'},
{id: 'silver',name: 'billy'},
];
var newUsers = [
{id: 'platinum',name: 'bob'},
{id: 'platinum',name: 'mike'},
{id: 'gold',name: 'will'},
{id: 'gold',name: 'jerry'},
];
const idsToReplace = {}
const result = [...newUsers]
result.forEach(u => {
idsToReplace[u.id] = true
})
userCategory.forEach(u => {
if(!idsToReplace[u.id]){
result.push(u)
}
})
console.log(result)
How do I flatten the nested Array in the Array?
Here is the example input Array,
const input = [
{
id: 1,
name: 'Charles',
otherFields: [{
id: 2,
name: 'Pung',
}, {
id: 3,
name: 'James',
}]
}, {
id: 4,
name: 'Charles',
otherFields: [{
id: 5,
name: 'Pung',
}, {
id: 6,
name: 'James',
}]
}
]
Output Array I want to get.
[{
id: 1,
name: 'Charles'
}, {
id: 2,
name: 'Pung',
}, {
id: 3,
name: 'James',
}, {
id: 4,
name: 'Charles'
}, {
id: 5,
name: 'Pung',
}, {
id: 6,
name: 'James',
}]
I want to somehow get the output in one statement like
input.map((sth) => ({...sth??, sth.field...})); // I'm not sure :(
With flatMap you can take out the otherFields property, and returning an array containing the parent item and the other array:
const input = [{
id: 1,
name: 'Charles',
otherFields: [{
id: 2,
name: 'Pung',
}, {
id: 3,
name: 'James',
}]
}];
console.log(
input.flatMap(({ otherFields, ...item }) => [item, ...otherFields])
);
For more than one level, you could take a recursive approach of flattening.
const
flat = ({ otherFields = [], ...o }) => [o, ...otherFields.flatMap(flat)],
input = [{ id: 1, name: 'Charles', otherFields: [{ id: 2, name: 'Pung' }, { id: 3, name: 'James', otherFields: [{ id: 4, name: 'Jane' }] }] }],
result = input.flatMap(flat);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have an array:
[
{ id: 1,
name: "parent1",
children: [
{ id: 10,
name: "first_child_of_id_1",
children: [
{ id: 100, name: "child_of_id_10", children: []},
{ id: 141, name: "child_of_id_10", children: []},
{ id: 155, name: "child_of_id_10", children: []}
]
},
{ id: 42,
name: "second_child_of_id_1",
children: [
{ id: 122, name: "child_of_id_42", children: []},
{ id: 133, name: "child_of_id_42", children: []},
{ id: 177, name: "child_of_id_42", children: []}
]
}
]
},
{ id: 7,
name: "parent7",
children: [
{ id: 74,
name: "first_child_of_id_7",
children: [
{ id: 700, name: "child_of_id_74", children: []},
{ id: 732, name: "child_of_id_74", children: []},
{ id: 755, name: "child_of_id_74", children: []}
]
},
{ id: 80,
name: "second_child_of_id_7",
children: [
{ id: 22, name: "child_of_id_80", children: []},
{ id: 33, name: "child_of_id_80", children: []},
{ id: 77, name: "child_of_id_80", children: []}
]
}
]
}
]
What I need is an array of arrays like this:
[
[ "id", "name", "parent_id", "parent_name" ],
[ 1, "parent1", null, "" ],
[ 10, "first_child_of_id_1", 1, "parent1"],
[ 42, "second_child_of_id_1", 1, "parent1"],
[100, "child_of_id_10", 10, "first_child_of_id_1"]
]
and so on for all nested objects for me to convert them into CSV rows. I've checked many answers and found a similar problem here: How to convert array of nested objects to CSV?
But it produces too long rows for many nested objects and I am not experienced enough with JavaScript to modify map function.
const categories = [
{ id: 1,
name: "parent1",
children: [
{ id: 10,
name: "first_child_of_id_1",
children: [
{ id: 100, name: "child_of_id_10", children: []},
{ id: 141, name: "child_of_id_10", children: []},
{ id: 155, name: "child_of_id_10", children: []}
]
},
{ id: 42,
name: "second_child_of_id_1",
children: [
{ id: 122, name: "child_of_id_42", children: []},
{ id: 133, name: "child_of_id_42", children: []},
{ id: 177, name: "child_of_id_42", children: []}
]
}
]
},
{ id: 7,
name: "parent7",
children: [
{ id: 74,
name: "first_child_of_id_7",
children: [
{ id: 700, name: "child_of_id_74", children: []},
{ id: 732, name: "child_of_id_74", children: []},
{ id: 755, name: "child_of_id_74", children: []}
]
},
{ id: 80,
name: "second_child_of_id_7",
children: [
{ id: 22, name: "child_of_id_80", children: []},
{ id: 33, name: "child_of_id_80", children: []},
{ id: 77, name: "child_of_id_80", children: []}
]
}
]
}
]
function pivot(arr) {
var mp = new Map();
function setValue(a, path, val) {
if (Object(val) !== val) { // primitive value
var pathStr = path.join('.');
var i = (mp.has(pathStr) ? mp : mp.set(pathStr, mp.size)).get(pathStr);
a[i] = val;
} else {
for (var key in val) {
setValue(a, key == '0' ? path : path.concat(key), val[key]);
}
}
return a;
}
var result = arr.map(obj => setValue([], [], obj));
return [[...mp.keys()], ...result];
}
function toCsv(arr) {
return arr.map(row =>
row.map(val => isNaN(val) ? JSON.stringify(val) : +val).join(',')
).join('\n');
}
<button onclick="console.log(toCsv(pivot(categories)))">Output</button>
Simple DFS or BFS algorithm should do the job here.
The difference is the order of created "rows". If you want to have all children of given node listed immediately after their parent, then you need to use BFS.
Example with DFS and BFS:
const input = [{
id: 1,
name: "parent1",
children: [{
id: 10,
name: "first_child_of_id_1",
children: [{
id: 100,
name: "child_of_id_10",
children: []
},
{
id: 141,
name: "child_of_id_10",
children: []
},
{
id: 155,
name: "child_of_id_10",
children: []
}
]
},
{
id: 42,
name: "second_child_of_id_1",
children: [{
id: 122,
name: "child_of_id_42",
children: []
},
{
id: 133,
name: "child_of_id_42",
children: []
},
{
id: 177,
name: "child_of_id_42",
children: []
}
]
}
]
},
{
id: 7,
name: "parent7",
children: [{
id: 74,
name: "first_child_of_id_7",
children: [{
id: 700,
name: "child_of_id_74",
children: []
},
{
id: 732,
name: "child_of_id_74",
children: []
},
{
id: 755,
name: "child_of_id_74",
children: []
}
]
},
{
id: 80,
name: "second_child_of_id_1",
children: [{
id: 22,
name: "child_of_id_80",
children: []
},
{
id: 33,
name: "child_of_id_80",
children: []
},
{
id: 77,
name: "child_of_id_80",
children: []
}
]
}
]
}
]
//DFS
function deepWalk(node, parent, output = []) {
if (!node || typeof node !== 'object' || !node.id) return;
output.push([node.id, node.name, parent ? parent.id : null, parent ? parent.name : ""])
if (node.children) {
for (const child of node.children) {
deepWalk(child, node, output);
}
}
return output;
}
//BFS
function broadWalk(root) {
const output = []
const queue = [];
queue.push({
node: root,
parent: null
});
while (queue.length) {
const {
node,
parent
} = queue.shift();
output.push([node.id, node.name, parent ? parent.id : null, parent ? parent.name : ""])
if (node.children) {
for (const child of node.children) {
queue.push({
node: child,
parent: node
});
}
}
}
return output;
}
let rowsDfs = [
["id", "name", "parent_id", "parent_name"]
];
let rowsBfs = [
["id", "name", "parent_id", "parent_name"]
];
for (const node of input) {
rowsDfs = [...rowsDfs, ...deepWalk(node)];
rowsBfs = [...rowsBfs, ...broadWalk(node)];
}
console.log("rows DFS: ", rowsDfs)
console.log("rows BFS: ", rowsBfs)
I want to keep the parent-child relationship of the tree node.
I have a JSON tree, like this
{
id: null,
children: [
{
id: 1,
children: [
{
id: 11,
children: [
{
id: 111
children: []
}
]
},
{
id: '12',
children: []
},
]
},
{
id: '2',
children: [
{
id: '21',
children: []
},
{
id: '22',
children: [
{
id: '221',
children: []
}
]
},
]
},
]
}
I want flat the tree, like this
[
{ id: 1, parent: null,},
{ id: 11, parent: 1, },
{ id: 111, parent: 11, },
{ id: 2, parent: null, },
{ id: 21, parent: 2, },
...
]
parent automatic generated
Is there any good way?
You could use flatMap method and create recursive function that will return 1D array as a result.
const data = {"id":null,"children":[{"id":1,"children":[{"id":11,"children":[{"id":111,"children":[]}]},{"id":"12","children":[]}]},{"id":"2","children":[{"id":"21","children":[]},{"id":"22","children":[{"id":"221","children":[]}]}]}]}
const flatten = (data, parent = null) =>
data.flatMap(({ id, children }) => ([
{ id, parent },
...flatten(children, id)
]))
const result = flatten(data.children);
console.log(result)
You could get the object and return an array of the flat children.
const
getFlat = ({ id, children = [] }) =>
children.flatMap(o => [{ id: o.id, parent: id }, ...getFlat(o)]);
var data = { id: null, children: [{ id: 1, children: [{ id: 11, children: [{ id: 111, children: [] }] }, { id: '12', children: [] }] }, { id: '2', children: [{ id: '21', children: [] }, { id: '22', children: [{ id: '221', children: [] }] }] }] },
flat = getFlat(data);
console.log(flat);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If compatbility with Internet Explorer is important, then the following approach which avoids the need for Array#flatMap might suit:
const input={id:null,children:[{id:'1',children:[{id:'11',children:[{id:'111',children:[]}]},{id:'12',children:[]}]},{id:'2',children:[{id:'21',children:[]},{id:'22',children:[{id:'221',children:[]}]}]}]};
const result = [];
// Define recursive function to walk through the data tree
// and produce a flat array of required data
const recurse = (item, parent) => {
// Only add item to result if it has valid id
if (item.id) {
result.push({
id: item.id,
parent: parent ? parent.id : null
});
}
// Iterate the children of this item, traversing and
// processing them in the same way
item.children.forEach(child => recurse(child, item))
}
recurse(input);
console.log(result);
You can use a simple recursion to achieve this
let data = {
id: 1,
name: 'a1',
children: [{
id: 2,
name: 'a2',
children: [{
id: 3,
name: 'b1',
children: []
},
{
id: 4,
name: 'b2',
children: []
}
]
}]
}
function flatten(data){
output = [];
return (function indentHandler(data, level, parent){
output.push({id: data['id'], parent: parent})
if (data['children'].length === 0){
return output;
}
level += 1;
data['children'].forEach(function(child){
return indentHandler(child, level, data.name);
});
return output;
})(data, 0, null);
}
flatten(data);
use recursion
var tree = {
id: null,
children: [{
id: 1,
children: [{
id: 11,
children: [{
id: 111,
children: []
}]
},
{
id: '12',
children: []
},
]
},
{
id: '2',
children: [{
id: '21',
children: []
},
{
id: '22',
children: [{
id: '221',
children: []
}]
},
]
}
]
};
var flat = [];
function flatArray(arr, parentId) {
arr.forEach(function(el) {
flat.push({
id: el.id,
parent: parentId || null
});
if (el.children)
flatArray(el.children, el.id);
});
}
flatArray(tree.children, 0)
console.log(flat);
I want to parse a json that is fetch from api
I have following schema
nodes = [
{
id: 1,
name: 'root1',
key: 'value1'
key1: 'value2'
children: [
{ id: 2, name: 'child1' },
{ id: 3, name: 'child2' }
]
},
{
id: 4,
name: 'root2',
key3: 'value3'
key4: 'value4'
children: [
{ id: 5, name: 'child2.1' },
{
id: 6,
name: 'child2.2',
key5: 'value5'
key6: 'value6'
children: [{
id: 7,
name: 'subsub',
children: [{
id: 8,
name: 'subsubsub'
}]
}]
}
]
}
];
I'm trying following but not working
data = []
var nodesFun = function(n, data, cObj) {
let obj = {}
obj["name"] = n['name']
if (n['children']) {
console.log(obj);
console.log("name--> "+n['name']);
let childList = []
for (let i=0; i < n['children'].length; i++){
console.log("cname--> "+n['children'][i]['name']);
childList.push({"name": n['children'][i]['name']})
let dataObj = nodesFun(n['children'][i], data, obj)
if (dataObj){
data.push(dataObj)
}
}
obj["children"] = childList
cObj["children"] = obj
return cObj
}else{
cObj["children"] = obj
return cObj
}
}
nodesFun(nodes, data, {})
console.log(nodes);
Now I want to convert above json in following format using recursive function
nodes = [{
id: 1,
name: 'root1',
children: [
{ id: 2, name: 'child1' },
{ id: 3, name: 'child2' }
]
},
{
id: 4,
name: 'root2',
children: [
{ id: 5, name: 'child2.1' },
{
id: 6,
name: 'child2.2',
children: [{
id: 7,
name: 'subsub',
children: [{
id: 8,
name: 'subsubsub'
}]
}]
}
]
}
];
You can modify your original nodes array using array#forEach and recursive approach. Iterate through each key of node if the key includes key word delete it and if it contains children call deleteKey function again.
var nodes = [ { id: 1, name: 'root1', key: 'value1', key1: 'value2', children: [ { id: 2, name: 'child1' }, { id: 3, name: 'child2' } ] }, { id: 4, name: 'root2', key3: 'value3', key4: 'value4', children: [ { id: 5, name: 'child2.1' }, { id: 6, name: 'child2.2',key5: 'value5', key6: 'value6', children: [{ id: 7, name: 'subsub', children: [{ id: 8, name: 'subsubsub' }] }] } ] } ];
var deleteKey = (nodes) => {
nodes.forEach(node => {
Object.keys(node).forEach(k => {
if(k.includes('key'))
delete node[k];
if(k == 'children')
deleteKey(node[k]);
})
});
}
deleteKey(nodes);
console.log(nodes);
.as-console-wrapper { max-height: 100% !important; top: 0; }
In case you want a new array, you can use array#reduce and array#map.
var nodes = [ { id: 1, name: 'root1', key: 'value1', key1: 'value2', children: [ { id: 2, name: 'child1' }, { id: 3, name: 'child2' } ] }, { id: 4, name: 'root2', key3: 'value3', key4: 'value4', children: [ { id: 5, name: 'child2.1' }, { id: 6, name: 'child2.2',key5: 'value5', key6: 'value6', children: [{ id: 7, name: 'subsub', children: [{ id: 8, name: 'subsubsub' }] }] } ] } ];
var removeKey = (nodes) => {
return nodes.map(node => {
return Object.keys(node).reduce((r, k) => {
if(!k.includes('key'))
r[k] = node[k];
if(k == 'children')
r[k] = removeKey(node[k]);
return r;
},{});
});
}
console.log(removeKey(nodes));
.as-console-wrapper { max-height: 100% !important; top: 0; }
The function delete all the keys that are not id, name and children from your object
let nodes = [
{
id: 1,
name: 'root1',
key: 'value1',
key1: 'value2',
children: [
{ id: 2, name: 'child1' },
{ id: 3, name: 'child2' }
]
},
{
id: 4,
name: 'root2',
key3: 'value3',
key4: 'value4',
children: [
{ id: 5, name: 'child2.1' },
{
id: 6,
name: 'child2.2',
key5: 'value5',
key6: 'value6',
children: [{
id: 7,
name: 'subsub',
children: [{
id: 8,
name: 'subsubsub'
}]
}]
}
]
}
];
const getFinalNode = function(arr){
arr.forEach(function(obj, idx){
let tmpObj = {
id: obj.id,
name: obj.name,
}
if(obj.children && obj.children.length){
tmpObj.children = getFinalNode(obj.children)
}
arr[idx] = tmpObj
})
return arr
}
getFinalNode(nodes);
console.log(nodes)