Deep Flatten JavaScript Object Recursively - javascript

Data:
var data = [
{
"id": 1,
"level": "1",
"text": "Sammy",
"type": "Item",
"items": [
{
"id": 11,
"level": "2",
"text": "Table",
"type": "Item",
"items": [
{
"id": 111,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 112,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
}
]
},
{
"id": 12,
"level": "2",
"text": "Chair",
"type": "Item",
"items": [
{
"id": 121,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 122,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
}
]
}
]
},
{
"id": 2,
"level": "1",
"text": "Sundy",
"type": "Item",
"items": [
{
"id": 21,
"level": "2",
"text": "MTable",
"type": "Item",
"items": [
{
"id": 211,
"level": "3",
"text": "MTDog",
"type": "Item",
"items": null
},
{
"id": 212,
"level": "3",
"text": "MTCat",
"type": "Item",
"items": null
}
]
},
{
"id": 22,
"level": "2",
"text": "MChair",
"type": "Item",
"items": [
{
"id": 221,
"level": "3",
"text": "MCDog",
"type": "Item",
"items": null
},
{
"id": 222,
"level": "3",
"text": "MCCat",
"type": "Item",
"items": null
}
]
}
]
},
{
"id": 3,
"level": "1",
"text": "Bruce",
"type": "Folder",
"items": [
{
"id": 31,
"level": "2",
"text": "BTable",
"type": "Item",
"items": [
{
"id": 311,
"level": "3",
"text": "BTDog",
"type": "Item",
"items": null
},
{
"id": 312,
"level": "3",
"text": "BTCat",
"type": "Item",
"items": null
}
]
},
{
"id": 32,
"level": "2",
"text": "Chair",
"type": "Item",
"items": [
{
"id": 321,
"level": "3",
"text": "BCDog",
"type": "Item",
"items": null
},
{
"id": 322,
"level": "3",
"text": "BCCat",
"type": "Item",
"items": null
}
]
}
]
}
];
Code:
var fdr = [];
var fd = function(n) {
if (n.items) {
_.forEach(n.items, function (value){
fd(value);
});
}
fdr.push(n);
};
_.forEach(data, fd);
console.log(fdr);
Desired output:
var data = [
{
"id": 1,
"level": "1",
"text": "Sammy",
"type": "Item",
"items": []
},
{
"id": 11,
"level": "2",
"text": "Table",
"type": "Item",
"items": []
},
{
"id": 111,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 112,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
},
{
"id": 12,
"level": "2",
"text": "Chair",
"type": "Item",
"items": []
},
{
"id": 121,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 122,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
},
{
"id": 2,
"level": "1",
"text": "Sundy",
"type": "Item",
"items": []
},
{
"id": 21,
"level": "2",
"text": "MTable",
"type": "Item",
"items": []
},
{
"id": 211,
"level": "3",
"text": "MTDog",
"type": "Item",
"items": null
},
{
"id": 212,
"level": "3",
"text": "MTCat",
"type": "Item",
"items": null
},
{
"id": 22,
"level": "2",
"text": "MChair",
"type": "Item",
"items": []
},
{
"id": 221,
"level": "3",
"text": "MCDog",
"type": "Item",
"items": null
},
{
"id": 222,
"level": "3",
"text": "MCCat",
"type": "Item",
"items": null
},
{
"id": 3,
"level": "1",
"text": "Bruce",
"type": "Folder",
"items": []
},
{
"id": 31,
"level": "2",
"text": "BTable",
"type": "Item",
"items": []
},
{
"id": 311,
"level": "3",
"text": "BTDog",
"type": "Item",
"items": null
},
{
"id": 312,
"level": "3",
"text": "BTCat",
"type": "Item",
"items": null
},
{
"id": 32,
"level": "2",
"text": "Chair",
"type": "Item",
"items": []
},
{
"id": 321,
"level": "3",
"text": "BCDog",
"type": "Item",
"items": null
},
{
"id": 322,
"level": "3",
"text": "BCCat",
"type": "Item",
"items": null
}
];
Conditions:
Object have unknowns level. Some child item may have one level down and some could have up to 5.
Questions
The fd function in the code is what I have come up with. I believe there's a 'cleaner' way to do this, just can't think of something. Plus, the function return items object, render it circular object.
JsBin:
http://jsbin.com/debojiqove/2/edit?html,js,output
Is there a way to flatten object recursively with lodash or just plain JavaScript?.

A solution in plain Javascript with respect to the items. It does not mutate the source array.
function flat(r, a) {
var b = {};
Object.keys(a).forEach(function (k) {
if (k !== 'items') {
b[k] = a[k];
}
});
r.push(b);
if (Array.isArray(a.items)) {
b.items = a.items.map(function (a) { return a.id; });
return a.items.reduce(flat, r);
}
return r;
}
var data = [{ "id": 1, "level": "1", "text": "Sammy", "type": "Item", "items": [{ "id": 11, "level": "2", "text": "Table", "type": "Item", "items": [{ "id": 111, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 112, "level": "3", "text": "Cat", "type": "Item", "items": null }] }, { "id": 12, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 121, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 122, "level": "3", "text": "Cat", "type": "Item", "items": null }] }] }, { "id": 2, "level": "1", "text": "Sundy", "type": "Item", "items": [{ "id": 21, "level": "2", "text": "MTable", "type": "Item", "items": [{ "id": 211, "level": "3", "text": "MTDog", "type": "Item", "items": null }, { "id": 212, "level": "3", "text": "MTCat", "type": "Item", "items": null }] }, { "id": 22, "level": "2", "text": "MChair", "type": "Item", "items": [{ "id": 221, "level": "3", "text": "MCDog", "type": "Item", "items": null }, { "id": 222, "level": "3", "text": "MCCat", "type": "Item", "items": null }] }] }, { "id": 3, "level": "1", "text": "Bruce", "type": "Folder", "items": [{ "id": 31, "level": "2", "text": "BTable", "type": "Item", "items": [{ "id": 311, "level": "3", "text": "BTDog", "type": "Item", "items": null }, { "id": 312, "level": "3", "text": "BTCat", "type": "Item", "items": null }] }, { "id": 32, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 321, "level": "3", "text": "BCDog", "type": "Item", "items": null }, { "id": 322, "level": "3", "text": "BCCat", "type": "Item", "items": null }] }] }];
document.write('<pre>' + JSON.stringify(data.reduce(flat, []), 0, 4) + '</pre>');

With a bit of ES6 flavor
function flatten(xs) {
return xs.reduce((acc, x) => {
acc = acc.concat(x);
if (x.items) {
acc = acc.concat(flatten(x.items));
x.items = [];
}
return acc;
}, []);
}

Using _.flatMapDeep (available since Lodash 4.7):
var flatten = function(item) {
return [item, _.flatMapDeep(item.items, flatten)];
}
var result = _.flatMapDeep(data, flatten);

A shorter solution using reduce and recursion
function flatten(data){
return data.reduce(function(result,next){
result.push(next);
if(next.items){
result = result.concat(flatten(next.items));
next.items = [];
}
return result;
},[]);
}
var data = [
{
"id": 1,
"level": "1",
"text": "Sammy",
"type": "Item",
"items": [
{
"id": 11,
"level": "2",
"text": "Table",
"type": "Item",
"items": [
{
"id": 111,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 112,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
}
]
},
{
"id": 12,
"level": "2",
"text": "Chair",
"type": "Item",
"items": [
{
"id": 121,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 122,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
}
]
}
]
},
{
"id": 2,
"level": "1",
"text": "Sundy",
"type": "Item",
"items": [
{
"id": 21,
"level": "2",
"text": "MTable",
"type": "Item",
"items": [
{
"id": 211,
"level": "3",
"text": "MTDog",
"type": "Item",
"items": null
},
{
"id": 212,
"level": "3",
"text": "MTCat",
"type": "Item",
"items": null
}
]
},
{
"id": 22,
"level": "2",
"text": "MChair",
"type": "Item",
"items": [
{
"id": 221,
"level": "3",
"text": "MCDog",
"type": "Item",
"items": null
},
{
"id": 222,
"level": "3",
"text": "MCCat",
"type": "Item",
"items": null
}
]
}
]
},
{
"id": 3,
"level": "1",
"text": "Bruce",
"type": "Folder",
"items": [
{
"id": 31,
"level": "2",
"text": "BTable",
"type": "Item",
"items": [
{
"id": 311,
"level": "3",
"text": "BTDog",
"type": "Item",
"items": null
},
{
"id": 312,
"level": "3",
"text": "BTCat",
"type": "Item",
"items": null
}
]
},
{
"id": 32,
"level": "2",
"text": "Chair",
"type": "Item",
"items": [
{
"id": 321,
"level": "3",
"text": "BCDog",
"type": "Item",
"items": null
},
{
"id": 322,
"level": "3",
"text": "BCCat",
"type": "Item",
"items": null
}
]
}
]
}
];
var result = flatten(data);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');

Plain JavaScript
var data = [{ "id": 1, "level": "1", "text": "Sammy", "type": "Item", "items": [{ "id": 11, "level": "2", "text": "Table", "type": "Item", "items": [{ "id": 111, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 112, "level": "3", "text": "Cat", "type": "Item", "items": null }] }, { "id": 12, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 121, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 122, "level": "3", "text": "Cat", "type": "Item", "items": null }] }] }, { "id": 2, "level": "1", "text": "Sundy", "type": "Item", "items": [{ "id": 21, "level": "2", "text": "MTable", "type": "Item", "items": [{ "id": 211, "level": "3", "text": "MTDog", "type": "Item", "items": null }, { "id": 212, "level": "3", "text": "MTCat", "type": "Item", "items": null }] }, { "id": 22, "level": "2", "text": "MChair", "type": "Item", "items": [{ "id": 221, "level": "3", "text": "MCDog", "type": "Item", "items": null }, { "id": 222, "level": "3", "text": "MCCat", "type": "Item", "items": null }] }] }, { "id": 3, "level": "1", "text": "Bruce", "type": "Folder", "items": [{ "id": 31, "level": "2", "text": "BTable", "type": "Item", "items": [{ "id": 311, "level": "3", "text": "BTDog", "type": "Item", "items": null }, { "id": 312, "level": "3", "text": "BTCat", "type": "Item", "items": null }] }, { "id": 32, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 321, "level": "3", "text": "BCDog", "type": "Item", "items": null }, { "id": 322, "level": "3", "text": "BCCat", "type": "Item", "items": null }] }] }];
var r = [];
function flatten(a) {
if (a.length == 0) return;
var o = {};
o.id = a[0].id;
o.level = a[0].level;
o.text = a[0].text;
o.type = a[0].type
o.items = a[0].items == null ? null : []
r.push(o);
if (Array.isArray(a[0].items)) {
flatten(a[0].items);
}
a.shift();
flatten(a);
}
flatten(data);
document.write('<pre>' + JSON.stringify(r, 0, 2) + '</pre>');

Here is solution using recursive function which I called flattenNestedObjectsArray()(for native JavaScript):
function flattenNestedObjectsArray(arr, part){
var flattened = part || [], items;
arr.forEach(function(v){
if (Array.isArray(v.items) && v.items.length) {
items = v.items;
v.items = [];
flattened.push(v);
flattened.concat(flattened, flattenNestedObjectsArray(items, flattened));
} else {
flattened.push(v);
}
});
return flattened;
}
var flattened = flattenNestedObjectsArray(data);
console.log(JSON.stringify(flattened, 0, 4));
The console.log output:
[
{
"id": 1,
"level": "1",
"text": "Sammy",
"type": "Item",
"items": []
},
{
"id": 11,
"level": "2",
"text": "Table",
"type": "Item",
"items": []
},
{
"id": 111,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 112,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
},
{
"id": 12,
"level": "2",
"text": "Chair",
"type": "Item",
"items": []
},
{
"id": 121,
"level": "3",
"text": "Dog",
"type": "Item",
"items": null
},
{
"id": 122,
"level": "3",
"text": "Cat",
"type": "Item",
"items": null
},
{
"id": 2,
"level": "1",
"text": "Sundy",
"type": "Item",
"items": []
},
{
"id": 21,
"level": "2",
"text": "MTable",
"type": "Item",
"items": []
},
{
"id": 211,
"level": "3",
"text": "MTDog",
"type": "Item",
"items": null
},
{
"id": 212,
"level": "3",
"text": "MTCat",
"type": "Item",
"items": null
},
{
"id": 22,
"level": "2",
"text": "MChair",
"type": "Item",
"items": []
},
{
"id": 221,
"level": "3",
"text": "MCDog",
"type": "Item",
"items": null
},
{
"id": 222,
"level": "3",
"text": "MCCat",
"type": "Item",
"items": null
},
{
"id": 3,
"level": "1",
"text": "Bruce",
"type": "Folder",
"items": []
},
{
"id": 31,
"level": "2",
"text": "BTable",
"type": "Item",
"items": []
},
{
"id": 311,
"level": "3",
"text": "BTDog",
"type": "Item",
"items": null
},
{
"id": 312,
"level": "3",
"text": "BTCat",
"type": "Item",
"items": null
},
{
"id": 32,
"level": "2",
"text": "Chair",
"type": "Item",
"items": []
},
{
"id": 321,
"level": "3",
"text": "BCDog",
"type": "Item",
"items": null
},
{
"id": 322,
"level": "3",
"text": "BCCat",
"type": "Item",
"items": null
}
]

I needed to do the same thing, and while solving my issue found a solution to yours using lodash:
function kids(node) {
return node.items
? [{...node, items: []}, _.map(node.items, kids)]
: {...node, items: null};
}
_.flatMapDeep(data, kids);

Here is my version of the recursive flattenItems function.
Note that I have removed the items property at all levels in the final result.
function flattenItems(data) {
// flat is the array that we will return by the end
var flat = [];
data.forEach(function(item) {
// get child properties only
var flatItem = {};
Object.keys(item).forEach(function(key) {
if(item[key] && item.hasOwnProperty(key) && !Array.isArray(item[key])) {
flatItem[key] = item[key];
}
// recursive flattern on subitems
// add recursive call results to the
// current stack version of "flat", by merging arrays
else if(Array.isArray(item[key])) {
Array.prototype.push.apply(flat, flattenItems(item[key]));
}
});
flat.push(flatItem);
});
// sort by level before returning
return flat.sort(function(i1, i2) {
return parseInt(i1.level) - parseInt(i2.level);
});
}
Here is a fiddle using your sample data, check the console.

Only a single liner function can do this job.
var data = [{ "id": 1, "level": "1", "text": "Sammy", "type": "Item", "items": [{ "id": 11, "level": "2", "text": "Table", "type": "Item", "items": [{ "id": 111, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 112, "level": "3", "text": "Cat", "type": "Item", "items": null }] }, { "id": 12, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 121, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 122, "level": "3", "text": "Cat", "type": "Item", "items": null }] }] }, { "id": 2, "level": "1", "text": "Sundy", "type": "Item", "items": [{ "id": 21, "level": "2", "text": "MTable", "type": "Item", "items": [{ "id": 211, "level": "3", "text": "MTDog", "type": "Item", "items": null }, { "id": 212, "level": "3", "text": "MTCat", "type": "Item", "items": null }] }, { "id": 22, "level": "2", "text": "MChair", "type": "Item", "items": [{ "id": 221, "level": "3", "text": "MCDog", "type": "Item", "items": null }, { "id": 222, "level": "3", "text": "MCCat", "type": "Item", "items": null }] }] }, { "id": 3, "level": "1", "text": "Bruce", "type": "Folder", "items": [{ "id": 31, "level": "2", "text": "BTable", "type": "Item", "items": [{ "id": 311, "level": "3", "text": "BTDog", "type": "Item", "items": null }, { "id": 312, "level": "3", "text": "BTCat", "type": "Item", "items": null }] }, { "id": 32, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 321, "level": "3", "text": "BCDog", "type": "Item", "items": null }, { "id": 322, "level": "3", "text": "BCCat", "type": "Item", "items": null }] }] }],
flatIron = (a,b) => a.reduce((p,c) => {!!c.items ? (p.push(c), flatIron(c.items,p), c.items = []) : p.push(c); return p},b),
flatArr = flatIron(data,[]);
document.write('<pre>' + JSON.stringify(flatArr, 0, 2) + '</pre>');

another way with recursive reducer function
_.reduce(data, function reducer(result, val) {
var items = _.reduce(val.items, reducer, []);
val.items = _.isArray(val.items) ? [] : val.items;
return _.concat(result, val, items);
}, []);

Modified Роман Парадеев answer to make it somewhat more dynamic.
function flatten(xs, childSelector) {
return xs.reduce((acc, x) => {
acc = acc.concat(x);
let children = childSelector(x);
if (children) {
acc = acc.concat(flatten(children, childSelector));
}
return acc;
}, []);
}
Now items is not hardcoded, and you can use it flatten(data, x => x.items).

Since Lo-Dash 3.0.0, _.flattenDeep(data) will return a deeply flattened array as you desire. There is also a _.flatten(data) function that flattens shallowly.

Since this older question has been brought back up, here is a modern version. It does not mutate (#Nina Scholz: or mutilate!) the original data, but returns a new array containing new objects.
const flattenItems = (xs) =>
xs. flatMap (({items, ... node}) => [node, ... flattenItems (items || [])])
const data = [{id: 1, level: "1", text: "Sammy", type: "Item", items: [{id: 11, level: "2", text: "Table", type: "Item", items: [{id: 111, level: "3", text: "Dog", type: "Item", items: null}, {id: 112, level: "3", text: "Cat", type: "Item", items: null}]}, {id: 12, level: "2", text: "Chair", type: "Item", items: [{id: 121, level: "3", text: "Dog", type: "Item", items: null}, {id: 122, level: "3", text: "Cat", type: "Item", items: null}]}]}, {id: 2, level: "1", text: "Sundy", type: "Item", items: [{id: 21, level: "2", text: "MTable", type: "Item", items: [{id: 211, level: "3", text: "MTDog", type: "Item", items: null}, {id: 212, level: "3", text: "MTCat", type: "Item", items: null}]}, {id: 22, level: "2", text: "MChair", type: "Item", items: [{id: 221, level: "3", text: "MCDog", type: "Item", items: null}, {id: 222, level: "3", text: "MCCat", type: "Item", items: null}]}]}, {id: 3, level: "1", text: "Bruce", type: "Folder", items: [{id: 31, level: "2", text: "BTable", type: "Item", items: [{id: 311, level: "3", text: "BTDog", type: "Item", items: null}, {id: 312, level: "3", text: "BTCat", type: "Item", items: null}]}, {id: 32, level: "2", text: "Chair", type: "Item", items: [{id: 321, level: "3", text: "BCDog", type: "Item", items: null}, {id: 322, level: "3", text: "BCCat", type: "Item", items: null}]}]}]
console .log (flattenItems (data))
.as-console-wrapper {max-height: 100% !important; top: 0}
This version does not include the fairly useless items property. It would be easy enough to add it in, with something like this:
const flattenItems = (xs) =>
xs. flatMap (({items, ... node}) => [
{... node, items: items == null ? null : []},
... flattenItems (items || [])
])

This solution should work on IE11; uses filter, map, and reduce.
var item = function(x) {
return {
"id": x.id,
"level": x.level,
"text": x.text,
"type": x.type,
"items": x.items ? [] : null
}
}
var flatten = function(a, b) {
return a.concat(b);
};
var onlyUnique = function(acc, curr) {
if (acc.length == 0) {
acc.push(curr);
} else {
var search = acc.filter(function(x){return x.id===curr.id;})
if (search.length == 0) {
acc.push(curr);
}
}
return acc;
}
var newData = data.map(function(x) {
return x.items.map(function(xx) {
return xx.items.map(function(xxx) {
return [item(x), item(xx), item(xxx)];
}).reduce(flatten, []);
}).reduce(flatten, [])
}).reduce(flatten, []).reduce(onlyUnique, []);;
console.log(JSON.stringify(newData, null, 2))

Simple with map and reduce :
export function flatten<T>(value: T): T[] {
const out: T[] = [];
if (value.children) {
const children = Object.values(value.children);
out.push(
value,
...children.map(flatten).reduce((acc, curVal) => {
return acc.concat(curVal);
}),
);
} else {
out.push(value);
}
return out;
}

Here is another solution using object-scan. This library is designed around traversing object hierarchies, so gives a lot of flexibility if this isn't exactly the result one needs.
.as-console-wrapper {max-height: 100% !important; top: 0}
<script type="module">
import objectScan from 'https://cdn.jsdelivr.net/npm/object-scan#18.4.0/lib/index.min.js';
const data = [{ id: 1, level: '1', text: 'Sammy', type: 'Item', items: [{ id: 11, level: '2', text: 'Table', type: 'Item', items: [{ id: 111, level: '3', text: 'Dog', type: 'Item', items: null }, { id: 112, level: '3', text: 'Cat', type: 'Item', items: null }] }, { id: 12, level: '2', text: 'Chair', type: 'Item', items: [{ id: 121, level: '3', text: 'Dog', type: 'Item', items: null }, { id: 122, level: '3', text: 'Cat', type: 'Item', items: null }] }] }, { id: 2, level: '1', text: 'Sundy', type: 'Item', items: [{ id: 21, level: '2', text: 'MTable', type: 'Item', items: [{ id: 211, level: '3', text: 'MTDog', type: 'Item', items: null }, { id: 212, level: '3', text: 'MTCat', type: 'Item', items: null }] }, { id: 22, level: '2', text: 'MChair', type: 'Item', items: [{ id: 221, level: '3', text: 'MCDog', type: 'Item', items: null }, { id: 222, level: '3', text: 'MCCat', type: 'Item', items: null }] }] }, { id: 3, level: '1', text: 'Bruce', type: 'Folder', items: [{ id: 31, level: '2', text: 'BTable', type: 'Item', items: [{ id: 311, level: '3', text: 'BTDog', type: 'Item', items: null }, { id: 312, level: '3', text: 'BTCat', type: 'Item', items: null }] }, { id: 32, level: '2', text: 'Chair', type: 'Item', items: [{ id: 321, level: '3', text: 'BCDog', type: 'Item', items: null }, { id: 322, level: '3', text: 'BCCat', type: 'Item', items: null }] }] }];
const fn = objectScan(['[*].**{items[*]}'], {
rtn: ({ value }) => ({ ...value, items: value.items === null ? null : [] }),
afterFn: ({ result }) => result.reverse()
});
const r = fn(data);
console.log(r);
/* => [
{ id: 1, level: '1', text: 'Sammy', type: 'Item', items: [] },
{ id: 11, level: '2', text: 'Table', type: 'Item', items: [] },
{ id: 111, level: '3', text: 'Dog', type: 'Item', items: null },
{ id: 112, level: '3', text: 'Cat', type: 'Item', items: null },
{ id: 12, level: '2', text: 'Chair', type: 'Item', items: [] },
{ id: 121, level: '3', text: 'Dog', type: 'Item', items: null },
{ id: 122, level: '3', text: 'Cat', type: 'Item', items: null },
{ id: 2, level: '1', text: 'Sundy', type: 'Item', items: [] },
{ id: 21, level: '2', text: 'MTable', type: 'Item', items: [] },
{ id: 211, level: '3', text: 'MTDog', type: 'Item', items: null },
{ id: 212, level: '3', text: 'MTCat', type: 'Item', items: null },
{ id: 22, level: '2', text: 'MChair', type: 'Item', items: [] },
{ id: 221, level: '3', text: 'MCDog', type: 'Item', items: null },
{ id: 222, level: '3', text: 'MCCat', type: 'Item', items: null },
{ id: 3, level: '1', text: 'Bruce', type: 'Folder', items: [] },
{ id: 31, level: '2', text: 'BTable', type: 'Item', items: [] },
{ id: 311, level: '3', text: 'BTDog', type: 'Item', items: null },
{ id: 312, level: '3', text: 'BTCat', type: 'Item', items: null },
{ id: 32, level: '2', text: 'Chair', type: 'Item', items: [] },
{ id: 321, level: '3', text: 'BCDog', type: 'Item', items: null },
{ id: 322, level: '3', text: 'BCCat', type: 'Item', items: null }
] */
</script>
Disclaimer: I'm the author of object-scan

Related

Transformation of nested object

I am new to JavaScript and Node JS
want to transform the following nested object with student
Data:
[{
"id": 1,
"name": "A",
"children": [{
"id": 2,
"name": "B",
"children": [{
"id": 3,
"name": "C"
},
{
"id": 4,
"name": "D"
}
]
}]
}]
to
Expected:
[{
"student": {
"id": 1,
"name": "A"
},
"children": [{
"student": {
"id": 2,
"name": "B"
},
"children": [{
"student": {
"id": 3,
"name": "C"
}
},
{
"student": {
"id": 4,
"name": "D"
}
}
]
}]
}]
I guess you are seeking a solution for an array with multiple student objects. So you can use the map method to modify them.
const original = [{
"id": 1,
"name": "A",
"children": [{
"id": 2,
"name": "B",
"children": [{
"id": 3,
"name": "C"
},
{
"id": 4,
"name": "D"
}
]
}]
}]
const modified = original.map(stu => {
return {
student: {
id: stu.id,
name: stu.name,
},
children: stu.children
}
})

Change Array to Object with recursives

i have an array shared below. I need to change this array like an object. I tried something but cant make it. I Want to make like this with foreach or recursive functions.
let nodes= [
{ id: 1, name: "Denny Curtis", title: "CEO", img: "https://cdn.balkan.app/shared/2.jpg" },
{ id: 2, pid: 1, name: "Ashley Barnett", title: "Sales Manager", img: "https://cdn.balkan.app/shared/3.jpg" },
{ id: 3, pid: 1, name: "Caden Ellison", title: "Dev Manager", img: "https://cdn.balkan.app/shared/4.jpg" }
];
let newObj = {
"id": 1,
"name": "Denny Curtis",
"title": "CEO",
"img": "https://cdn.balkan.app/shared/2.jpg",
"children": [
{
"id": 2,
"pid": 1,
"name": "Ashley Barnett",
"title": "Sales Manager",
"img": "https://cdn.balkan.app/shared/3.jpg",
"children": [
{
"id": 4,
"pid": 2,
"name": "Elliot Patel",
"title": "Sales",
"img": "https://cdn.balkan.app/shared/5.jpg"
},
{
"id": 5,
"pid": 2,
"name": "Lynn Hussain",
"title": "Sales",
"img": "https://cdn.balkan.app/shared/6.jpg"
}
]
}
]
}
Thank you from now.
It would probably be better to make an array of users with the children related to them.
let nodes = [{
id: 1,
name: "Denny Curtis",
title: "CEO",
img: "https://cdn.balkan.app/shared/2.jpg"
},
{
id: 2,
pid: 1,
name: "Ashley Barnett",
title: "Sales Manager",
img: "https://cdn.balkan.app/shared/3.jpg"
},
{
id: 3,
pid: 1,
name: "Caden Ellison",
title: "Dev Manager",
img: "https://cdn.balkan.app/shared/4.jpg"
}
];
let newNodeArray = [];
nodes.forEach(node=>{
newNodeArray.push({...node,children: nodes.filter(filterNode => filterNode.pid === node.id )});
});
console.log(JSON.stringify(newNodeArray));
The outcome of this one would look like:
[
{
"id":1,
"name":"Denny Curtis",
"title":"CEO",
"img":"https://cdn.balkan.app/shared/2.jpg",
"children":[
{
"id":2,
"pid":1,
"name":"Ashley Barnett",
"title":"Sales Manager",
"img":"https://cdn.balkan.app/shared/3.jpg"
},
{
"id":3,
"pid":1,
"name":"Caden Ellison",
"title":"Dev Manager",
"img":"https://cdn.balkan.app/shared/4.jpg"
}
]
},
{
"id":2,
"pid":1,
"name":"Ashley Barnett",
"title":"Sales Manager",
"img":"https://cdn.balkan.app/shared/3.jpg",
"children":[]
},
{
"id":3,
"pid":1,
"name":"Caden Ellison",
"title":"Dev Manager",
"img":"https://cdn.balkan.app/shared/4.jpg",
"children":[]
}
]
//This is how you would do it if you want the response to be an object
let nodeObject = {...nodes.filter(node=>!node.pid)[0],children:[]};
nodeObject.children = nodes.filter(node=>nodeObject.id === node.pid);
nodeObject.children = nodeObject.children.map(node=>({...node,children: nodes.filter(filterNode=>node.id === filterNode.pid)}));
console.log(nodeObject);
// The Response for this would be:
{
"id": 1,
"name": "Denny Curtis",
"title": "CEO",
"img": "https://cdn.balkan.app/shared/2.jpg",
"children": [{
"id": 2,
"pid": 1,
"name": "Ashley Barnett",
"title": "Sales Manager",
"img": "https://cdn.balkan.app/shared/3.jpg",
"children": [{
"id": 4,
"pid": 2,
"name": "Caden Ellison",
"title": "Sales",
"img": "https://cdn.balkan.app/shared/4.jpg"
}]
}, {
"id": 3,
"pid": 1,
"name": "Caden Ellison",
"title": "Dev Manager",
"img": "https://cdn.balkan.app/shared/4.jpg",
"children": []
}]
}

Build a map using values of multiple objects in an array javascript

I need to build a map "A" from an existing array of objects. However the key value pairs on Map A are from the values of existing Object keys "id" and "cap".
Is it possible to read the values of 2 keys and store as an object
var items = [{
"id": 1,
"name": "Primary",
"cap": [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}]
},{
"id": 2,
"name": "Secondary",
"cap": [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}
]
}]
My map needs to be like this
{ "1" : [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}],
"2" : [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}]
}
Use Array#reduce to achieve the results like below:
var items = [{
"id": 1,
"name": "Primary",
"cap": [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}]
}, {
"id": 2,
"name": "Secondary",
"cap": [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}]
}];
var ans = items.reduce(function(v, i) {
v[i.id] = i.cap;
return v;
}, {});
console.log(ans);
You can do this using a simple loop on the original array, and defining a new key: value pair into the object.
// Create the map
var map = {}
// For every 'item' within the 'items' array
items.forEach(item => {
// Map the item ID to the item.cap array
map[item.id] = item.cap
}
var items = [{
"id": 1,
"name": "Primary",
"cap": [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}]
}, {
"id": 2,
"name": "Secondary",
"cap": [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}]
}]
var map = {}
items.forEach(item => {
map[item.id] = item.cap
})
console.log(map)
Using ES6 Array.from() method.
var items = [{
"id": 1,
"name": "Primary",
"cap": [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}]
},{
"id": 2,
"name": "Secondary",
"cap": [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}
]
}];
var obj = {};
var res = Array.from(items, x => obj[x.id] = x.cap);
console.log(obj);

Flattening deeply nested array of objects

I got following data structure, which is an array of accounts objects, where some accounts are being parents to its children accounts, which in turn can be parents to other accounts etc.:
[{
"id": "acc.1260446672222.11",
"type": "EXPENSES_FOLDER",
"name": "Expense Group",
"balance": 3418.11,
"children": [{
"id": "acc.1260446672238.27",
"type": "EXPENSE",
"name": "Advertising, Promotion and Entertainment Account",
"balance": 0,
"children": []
}, {
"id": "acc.9a2492ba-0d82-4f4a-a1b4-14868f1e1a39",
"type": "EXPENSES_FOLDER",
"name": "Premises Costs",
"balance": 0,
"children": [{
"id": "acc.287ba5b6-5536-428b-950f-d71d2af73ccc",
"type": "EXPENSE",
"name": "Use of Home - Gas",
"balance": 0,
"children": [
]
}, {
"id": "acc.7091ee15-3f02-4bd1-94e5-5918cf986969",
"type": "EXPENSE",
"name": "Hire of Venue, Studios, Teaching Rooms",
"balance": 0,
"children": [
]
}]
}, {
"id": "acc.827ec446-edeb-4f2b-8032-d306292d2d83",
"type": "EXPENSES_FOLDER",
"name": "Administrative Expenses",
"balance": 558.61,
"children": [{
"id": "acc.0ed5fc81-7734-4452-86a9-db22a6b0f8e8",
"type": "EXPENSE",
"name": "Bank Charges",
"balance": 15,
"children": [
]
}, {
"id": "acc.e2cdb2c0-8565-4991-a35a-d4596b0ddf45",
"type": "EXPENSE",
"name": "Software & Computer Peripherals",
"balance": 417.13,
"children": [
]
}, {
"id": "acc.96d5d00e-43f4-4d3a-b97b-fdf258c65514",
"type": "EXPENSE",
"name": "Printing, photocopying etc",
"balance": 55.93,
"children": [
]
}, {
"id": "acc.494dd64a-4fb3-42b8-be3e-8f3b59a2ef59",
"type": "EXPENSE",
"name": "Artists Administration Service",
"balance": 0,
"children": [
]
}, {
"id": "acc.1260446672238.35",
"type": "EXPENSE",
"name": "Stationery",
"balance": 0,
"children": [
]
}, {
"id": "acc.96d89d0d-5465-488b-b37f-d41ca114c5e6",
"type": "EXPENSE",
"name": "Mobile Telephone",
"balance": 41.19,
"children": [
]
}, {
"id": "acc.1260446672238.33",
"type": "EXPENSE",
"name": "Home Telephone",
"balance": 0,
"children": [
]
}, {
"id": "acc.1260446672238.38",
"type": "EXPENSE",
"name": "Postage/delivery",
"balance": 29.36,
"children": [
]
}]
}, {
"id": "acc.b9c9bbc7-43df-472e-9ac8-c7c76f08f49a",
"type": "EXPENSES_FOLDER",
"name": "Instruments, Equipment Maintenance etc",
"balance": 1002.48,
"children": [{
"id": "acc.1260446672238.32",
"type": "OTHER_EXPENSES",
"name": "Instrument Insurance",
"balance": 157.48,
"children": [
]
}, {
"id": "acc.2a1cca15-2868-4770-a3e7-d43a6268c6a1",
"type": "EXPENSE",
"name": "Instrument Repairs & Maintenance",
"balance": 845,
"children": [
]
}, {
"id": "acc.a908aee0-84fb-450a-916b-4cec25265aef",
"type": "EXPENSE",
"name": "Accessories & Replacement Parts",
"balance": 0,
"children": [
]
}]
}, {
"id": "acc.a42cdd86-0d9e-4f3f-af0d-7c4525374731",
"type": "EXPENSES_FOLDER",
"name": "Motor Vehicle",
"balance": 0,
"children": [{
"id": "acc.cb325e7e-0ce4-4c78-9cb4-20659df733a6",
"type": "EXPENSE",
"name": "Fuel and Oil",
"balance": 0,
"children": [
]
}]
}, {
"id": "acc.4bdd9e26-ce64-4e7f-b46a-82ec9de06ded",
"type": "EXPENSES_FOLDER",
"name": "Other Travel",
"balance": 132.1,
"children": [{
"id": "acc.77dd2142-f2de-4a2c-9247-061d0661bc0a",
"type": "EXPENSE",
"name": "Taxis",
"balance": 24.5,
"children": [
]
}, {
"id": "acc.2b54abdd-7ef5-43cd-bdb9-c8c981b59ff2",
"type": "EXPENSE",
"name": "Public Transport",
"balance": 107.6,
"children": [
]
}]
}, {
"id": "acc.e4695b70-31fa-4e23-afd0-97335dcd5b9e",
"type": "EXPENSE",
"name": "Subsitence",
"balance": 0,
"children": [
]
}, {
"id": "acc.02d222bf-4dff-4308-afe9-69b93f412ada",
"type": "EXPENSE",
"name": "Hotel and Accomodation",
"balance": 0,
"children": [
]
}, {
"id": "acc.d61cd5b4-2c80-4ab8-93d0-9d5726bd253b",
"type": "EXPENSES_FOLDER",
"name": "Fees and Commission Paid",
"balance": 0,
"children": [{
"id": "acc.1262189019758.7",
"type": "EXPENSE",
"name": "Pupils exam entry fees",
"balance": 0,
"children": [
]
}, {
"id": "acc.a7d7efd3-d0da-4704-babb-079b6077f3fe",
"type": "EXPENSE",
"name": "Audition, competition entry fees",
"balance": 0,
"children": [
]
}, {
"id": "acc.3b91ee4e-40a8-46d8-aa05-3afa5974b3ef",
"type": "EXPENSE",
"name": "Deputies, Other Musicians",
"balance": 0,
"children": [
]
}]
}, {
"id": "acc.250d6872-6023-4599-a0b6-b7159eebbfa1",
"type": "EXPENSES_FOLDER",
"name": "Other Professional Expenses",
"balance": 1739.42,
"children": [{
"id": "acc.b7315228-f85a-4ffb-9199-d1128a409e5f",
"type": "EXPENSE",
"name": "Promotion & Publicity",
"balance": 138.6,
"children": [
]
}, {
"id": "acc.69ca2005-d7a0-448b-b70c-dafb128a48ae",
"type": "EXPENSE",
"name": "Other Expenses",
"balance": 364.5,
"children": [
]
}, {
"id": "acc.dcd999d2-4e18-41be-b9cc-218d4034b88e",
"type": "EXPENSE",
"name": "Office Equipment, Furniture",
"balance": 0,
"children": [
]
}, {
"id": "acc.e0460706-d5c9-4c40-9d1e-0d2058864b92",
"type": "EXPENSE",
"name": "CDs, Dowloads etc",
"balance": 67.57,
"children": [
]
}, {
"id": "acc.1866df79-9e44-459a-a978-727904987469",
"type": "EXPENSE",
"name": "Professional Books, Magazines",
"balance": 104.01,
"children": [
]
}, {
"id": "acc.24c1651d-e7ae-48bc-a32d-311427e0fcea",
"type": "EXPENSE",
"name": "Professional Associations",
"balance": 272.17,
"children": [
]
}, {
"id": "acc.289ab0ac-b9d3-435e-ac82-9da9702b7d4b",
"type": "EXPENSE",
"name": "Tuition",
"balance": 470,
"children": [
]
}, {
"id": "acc.f24cf99b-6291-4b9f-821e-425f4909d4e1",
"type": "EXPENSE",
"name": "Scores, Manuscript Paper etc",
"balance": 215.32,
"children": [
]
}, {
"id": "acc.1af95953-56f0-455e-9d0a-7c4e0477cf0d",
"type": "EXPENSE",
"name": "Performance Clothing",
"balance": 0,
"children": [
]
}, {
"id": "acc.c0585577-535a-4ae2-a02b-e5b249f67c67",
"type": "EXPENSE",
"name": "Concerts, Shows etc",
"balance": 107.25,
"children": [
]
}]
}, {
"id": "acc.1260446672222.24",
"type": "ADMIN",
"name": "Administrative Expenses",
"balance": 0,
"children": [
]
}, {
"id": "acc.1260446672238.26",
"type": "TRAVEL",
"name": "Travel and Subsistence Account",
"balance": -14.5,
"children": [
]
}, {
"id": "acc.1260446672238.28",
"type": "LEGAL",
"name": "Legal and Professional Costs Account",
"balance": 0,
"children": [
]
}, {
"id": "acc.1260446672238.36",
"type": "OTHER_EXPENSES",
"name": "Rent/Rates",
"balance": 0,
"children": [
]
}, {
"id": "acc.1262191376548.37",
"type": "EXPENSE",
"name": "Research",
"balance": 0,
"children": [
]
}, {
"id": "acc.1262191388329.38",
"type": "EXPENSE",
"name": "Professional Development",
"balance": 0,
"children": [
]
}, {
"id": "acc.1262192291558.52",
"type": "EXPENSE",
"name": "Professional Presentation",
"balance": 0,
"children": [
]
}, {
"id": "acc.1262193596634.72",
"type": "EXPENSE",
"name": "Subscriptions",
"balance": 0,
"children": [
]
}, {
"id": "acc.1262265941130.16",
"type": "EXPENSE",
"name": "Piano accompaniment",
"balance": 0,
"children": [
]
}, {
"id": "acc.1267370824329.1",
"type": "EXPENSE",
"name": "Cost of Sales",
"balance": 0,
"children": [
]
}]
}]
What I need is to flatten this array to have a flat list of accounts. What's the way to proceed with that in Vanilla JavaScript. (I also got access to lodash methods in my project).
Just iterate over and if a children is found get the array from the children concatinated.
function flat(array) {
var result = [];
array.forEach(function (a) {
result.push(a);
if (Array.isArray(a.children)) {
result = result.concat(flat(a.children));
}
});
return result;
}
var data = [{ id: "acc.1260446672222.11", type: "EXPENSES_FOLDER", name: "Expense Group", balance: 3418.11, children: [{ id: "acc.1260446672238.27", type: "EXPENSE", name: "Advertising, Promotion and Entertainment Account", balance: 0, children: [] }, { id: "acc.9a2492ba-0d82-4f4a-a1b4-14868f1e1a39", type: "EXPENSES_FOLDER", name: "Premises Costs", balance: 0, children: [{ id: "acc.287ba5b6-5536-428b-950f-d71d2af73ccc", type: "EXPENSE", name: "Use of Home - Gas", balance: 0, children: [] }, { id: "acc.7091ee15-3f02-4bd1-94e5-5918cf986969", type: "EXPENSE", name: "Hire of Venue, Studios, Teaching Rooms", balance: 0, children: [] }] }, { id: "acc.827ec446-edeb-4f2b-8032-d306292d2d83", type: "EXPENSES_FOLDER", name: "Administrative Expenses", balance: 558.61, children: [{ id: "acc.0ed5fc81-7734-4452-86a9-db22a6b0f8e8", type: "EXPENSE", name: "Bank Charges", balance: 15, children: [] }, { id: "acc.e2cdb2c0-8565-4991-a35a-d4596b0ddf45", type: "EXPENSE", name: "Software & Computer Peripherals", balance: 417.13, children: [] }, { id: "acc.96d5d00e-43f4-4d3a-b97b-fdf258c65514", type: "EXPENSE", name: "Printing, photocopying etc", balance: 55.93, children: [] }, { id: "acc.494dd64a-4fb3-42b8-be3e-8f3b59a2ef59", type: "EXPENSE", name: "Artists Administration Service", balance: 0, children: [] }, { id: "acc.1260446672238.35", type: "EXPENSE", name: "Stationery", balance: 0, children: [] }, { id: "acc.96d89d0d-5465-488b-b37f-d41ca114c5e6", type: "EXPENSE", name: "Mobile Telephone", balance: 41.19, children: [] }, { id: "acc.1260446672238.33", type: "EXPENSE", name: "Home Telephone", balance: 0, children: [] }, { id: "acc.1260446672238.38", type: "EXPENSE", name: "Postage/delivery", balance: 29.36, children: [] }] }, { id: "acc.b9c9bbc7-43df-472e-9ac8-c7c76f08f49a", type: "EXPENSES_FOLDER", name: "Instruments, Equipment Maintenance etc", balance: 1002.48, children: [{ id: "acc.1260446672238.32", type: "OTHER_EXPENSES", name: "Instrument Insurance", balance: 157.48, children: [] }, { id: "acc.2a1cca15-2868-4770-a3e7-d43a6268c6a1", type: "EXPENSE", name: "Instrument Repairs & Maintenance", balance: 845, children: [] }, { id: "acc.a908aee0-84fb-450a-916b-4cec25265aef", type: "EXPENSE", name: "Accessories & Replacement Parts", balance: 0, children: [] }] }, { id: "acc.a42cdd86-0d9e-4f3f-af0d-7c4525374731", type: "EXPENSES_FOLDER", name: "Motor Vehicle", balance: 0, children: [{ id: "acc.cb325e7e-0ce4-4c78-9cb4-20659df733a6", type: "EXPENSE", name: "Fuel and Oil", balance: 0, children: [] }] }, { id: "acc.4bdd9e26-ce64-4e7f-b46a-82ec9de06ded", type: "EXPENSES_FOLDER", name: "Other Travel", balance: 132.1, children: [{ id: "acc.77dd2142-f2de-4a2c-9247-061d0661bc0a", type: "EXPENSE", name: "Taxis", balance: 24.5, children: [] }, { id: "acc.2b54abdd-7ef5-43cd-bdb9-c8c981b59ff2", type: "EXPENSE", name: "Public Transport", balance: 107.6, children: [] }] }, { id: "acc.e4695b70-31fa-4e23-afd0-97335dcd5b9e", type: "EXPENSE", name: "Subsitence", balance: 0, children: [] }, { id: "acc.02d222bf-4dff-4308-afe9-69b93f412ada", type: "EXPENSE", name: "Hotel and Accomodation", balance: 0, children: [] }, { id: "acc.d61cd5b4-2c80-4ab8-93d0-9d5726bd253b", type: "EXPENSES_FOLDER", name: "Fees and Commission Paid", balance: 0, children: [{ id: "acc.1262189019758.7", type: "EXPENSE", name: "Pupils exam entry fees", balance: 0, children: [] }, { id: "acc.a7d7efd3-d0da-4704-babb-079b6077f3fe", type: "EXPENSE", name: "Audition, competition entry fees", balance: 0, children: [] }, { id: "acc.3b91ee4e-40a8-46d8-aa05-3afa5974b3ef", type: "EXPENSE", name: "Deputies, Other Musicians", balance: 0, children: [] }] }, { id: "acc.250d6872-6023-4599-a0b6-b7159eebbfa1", type: "EXPENSES_FOLDER", name: "Other Professional Expenses", balance: 1739.42, children: [{ id: "acc.b7315228-f85a-4ffb-9199-d1128a409e5f", type: "EXPENSE", name: "Promotion & Publicity", balance: 138.6, children: [] }, { id: "acc.69ca2005-d7a0-448b-b70c-dafb128a48ae", type: "EXPENSE", name: "Other Expenses", balance: 364.5, children: [] }, { id: "acc.dcd999d2-4e18-41be-b9cc-218d4034b88e", type: "EXPENSE", name: "Office Equipment, Furniture", balance: 0, children: [] }, { id: "acc.e0460706-d5c9-4c40-9d1e-0d2058864b92", type: "EXPENSE", name: "CDs, Dowloads etc", balance: 67.57, children: [] }, { id: "acc.1866df79-9e44-459a-a978-727904987469", type: "EXPENSE", name: "Professional Books, Magazines", balance: 104.01, children: [] }, { id: "acc.24c1651d-e7ae-48bc-a32d-311427e0fcea", type: "EXPENSE", name: "Professional Associations", balance: 272.17, children: [] }, { id: "acc.289ab0ac-b9d3-435e-ac82-9da9702b7d4b", type: "EXPENSE", name: "Tuition", balance: 470, children: [] }, { id: "acc.f24cf99b-6291-4b9f-821e-425f4909d4e1", type: "EXPENSE", name: "Scores, Manuscript Paper etc", balance: 215.32, children: [] }, { id: "acc.1af95953-56f0-455e-9d0a-7c4e0477cf0d", type: "EXPENSE", name: "Performance Clothing", balance: 0, children: [] }, { id: "acc.c0585577-535a-4ae2-a02b-e5b249f67c67", type: "EXPENSE", name: "Concerts, Shows etc", balance: 107.25, children: [] }] }, { id: "acc.1260446672222.24", type: "ADMIN", name: "Administrative Expenses", balance: 0, children: [] }, { id: "acc.1260446672238.26", type: "TRAVEL", name: "Travel and Subsistence Account", balance: -14.5, children: [] }, { id: "acc.1260446672238.28", type: "LEGAL", name: "Legal and Professional Costs Account", balance: 0, children: [] }, { id: "acc.1260446672238.36", type: "OTHER_EXPENSES", name: "Rent/Rates", balance: 0, children: [] }, { id: "acc.1262191376548.37", type: "EXPENSE", name: "Research", balance: 0, children: [] }, { id: "acc.1262191388329.38", type: "EXPENSE", name: "Professional Development", balance: 0, children: [] }, { id: "acc.1262192291558.52", type: "EXPENSE", name: "Professional Presentation", balance: 0, children: [] }, { id: "acc.1262193596634.72", type: "EXPENSE", name: "Subscriptions", balance: 0, children: [] }, { id: "acc.1262265941130.16", type: "EXPENSE", name: "Piano accompaniment", balance: 0, children: [] }, { id: "acc.1267370824329.1", type: "EXPENSE", name: "Cost of Sales", balance: 0, children: [] }] }],
result = flat(data);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
try to understand this version, with it's benefits and implications
function flatten(into, node){
if(node == null) return into;
if(Array.isArray(node)) return node.reduce(flatten, into);
into.push(node);
return flatten(into, node.children);
}
var out = flatten([], yourArray);
the argument order might be confusing at first, unless you are used to reduced, or the concept of options first, data last, wich is very handy when it comes to FP and currying or partial application.
You could make a recursive function that looks through each account, and if it has children calls itself on the children, the function should return a flattened array of accounts.
This example is a bit more complicated than it has to be because I am assuming that order matters and that the parent should be before the children.
function flattenAccounts(accounts){
var a = [];
for(var i=0;i<accounts.length;i++){
var o = accounts[i];
if(o.children){
var c = flattenAccounts(o.children);
if(c){
a = a.concat(c);
}
}
a.push(o)
}
return a;
}
This is a simpler example but the children would end up before the parent.
function flattenAccounts(accounts){
var a = [];
for(var i=0;i<accounts.length;i++){
if(accounts[i].children){
a = a.concat(flattenAccounts(accounts[i].children) )
}
a.push(accounts[i])
}
return a;
}
You can loop through the array entries, check if it's an array with isArray() and recursively flatten the entries of the array as below:
function deepFlattenArray(arr) {
const finalArray = [];
// Loop through the array contents
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
// Recursively flatten entries that are arrays and push into the finalArray
finalArray.push(...deepFlattenArray(arr[i]));
} else {
// Push entries that are not arrays
finalArray.push(arr[i]);
}
}
return finalArray;
}
I'd like to point out that all answers work by copying the children to the root, but leaving the original children in place, and hence creating duplicates - not a flat array.
Depending on how the data is used, this might lead to issues. I would certainly not want to be caught by my team leaving a messy array behind.
I couldn't find a solution to remove the children within the recursive function, so I added a second "cleanup" function.
Based on the accepted answer by Nina Scholz:
function flattenArr(arr) {
var dirtyArr = function(arr) {
let result = [];
arr.forEach((arrItem) => {
result.push(arrItem);
if (Array.isArray(arrItem.children)) {
result = result.concat(dirtyArr(arrItem.children));
}
});
return result;
};
var cleanArr = function(arr) {
arr.forEach((arrItem) => {
if (arrItem.hasOwnProperty('children')) {
delete arrItem.children;
}
});
return arr;
};
return cleanArr(dirtyArr(arr));
}
You can also use javascript recursive map function and get the children nodes of the nested array, like this:
const dataTree = [//array above];
const getMembers = (members) => {
let children = [];
const flattenMembers = members.map(m => {
if (m.children && m.children.length) {
children = [...children, ...m.children];
}
return m;
});
return flattenMembers.concat(children.length ? getMembers(children) : children);
};
getMembers(dataTree);
Source Here
Yet another approach.. It's simple but it works.
function flattenChildren(arr: any): any {
return arr.flatMap(({ children, ...o }: { children: any; o: any }) => [
o,
...flattenChildren(children),
])
}

Convert tree structure to hierarchical array

I have puzzling with this problem for hours. I am using Angular and angular-ui-tree to create an editable tree with unknown number of leaves.
I need to flatten and convert the tree into a hierarchical array in order to pass it to MongoDb. I am also using Underscore.js as a utility
var tree = [{
"_id": 1,
"title": "node1",
"nodes": [{
"_id": 11,
"title": "node1.1",
"nodes": [{
"_id": 111,
"title": "node1.1.1",
"children": [{
"_id": 1111,
"title": "node1.1.1.1",
"children": []
}]
}]
}, {
"_id": 12,
"title": "node1.2",
"children": []
}]
}, {
"_id": 2,
"title": "node2",
"children": [{
"id": 21,
"title": "node2.1",
"children": []
}, {
"_id": 22,
"title": "node2.2",
"children": []
}]
}, {
"_id": 3,
"title": "node3",
"children": [{
"id": 31,
"title": "node3.1",
"children": []
}]
}, {
"_id": 4,
"title": "node4",
"children": [{
"_id": 41,
"title": "node4.1",
"children": []
}]
}]
//desired output
//parentId is null for top leaf nodes, path is String made by the parents' ids
[
{ "_id": 1, "title": "node1", "parentId": null, "path" : ""},
{ "_id": 11, "title": "node1.1", "parentId": 1, "path" : ",1"},
{ "_id": 111, "title": "node1.1.1", "parentId": 11, "path" : ",1,11"},
{ "_id": 1111, "title": "node1.1.1.1", "parentId": 111, "path" : ",1,11,111"},
{ "_id": 12, "title": "node1.1", "parentId": 1, "path" : ",1"},
{ "_id": 2, "title": "node2", "parentId": null, "path" : ""},
{ "_id": 21, "title": "node2.1", "parentId": 2, "path" : ",2"},
{ "_id": 3, "title": "node3", "parentId": null, "path" : ""},
{ "_id": 31, "title": "node3.1", "parentId": 3, "path" : ",3"},
{ "_id": 4, "title": "node4", "parentId": null, "path" : ""},
{ "_id": 41, "title": "node4.1", "parentId": 4, "path" : ",4"},
]
I'd use recursion here to "walk" down the tree. Note that your input tree sometimes uses "children" and sometimes uses "nodes" to denote its array of children; I have changed it to "children" throughout.
var wholeTree = [{
"_id": 1,
"title": "node1",
"children": [{
"_id": 11,
"title": "node1.1",
"children": [{
"_id": 111,
"title": "node1.1.1",
"children": [{
"_id": 1111,
"title": "node1.1.1.1",
"children": []
}]
}]
}, {
"_id": 12,
"title": "node1.2",
"children": []
}]
}, {
"_id": 2,
"title": "node2",
"children": [{
"id": 21,
"title": "node2.1",
"children": []
}, {
"_id": 22,
"title": "node2.2",
"children": []
}]
}, {
"_id": 3,
"title": "node3",
"children": [{
"id": 31,
"title": "node3.1",
"children": []
}]
}, {
"_id": 4,
"title": "node4",
"children": [{
"_id": 41,
"title": "node4.1",
"children": []
}]
}];
var flattened = flattenTreeToNodes( wholeTree, null, "" );
$("#output").text( JSON.stringify(flattened) );
function flattenTreeToNodes( tree, parentId, basePath ) {
console.log( parentId, basePath );
function createFlattenedNode( treeNode ) {
var path = parentId?basePath + "," + parentId:"";
return {
"_id": treeNode._id,
title: treeNode.title,
parentId: parentId,
path: path
}
}
var nodes = [];
for(var i=0; i<tree.length; i++) {
var treeNode = tree[i];
var flattenedNode = createFlattenedNode(treeNode);
nodes.push ( flattenedNode );
var flattenedChildren = flattenTreeToNodes( treeNode.children, treeNode._id, flattenedNode.path );
nodes = nodes.concat( flattenedChildren );
}
return nodes;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<pre id='output'>
</pre>
A Simple recursive function will do the job :
var out = [
];
var generate = function (collection, parent) {
for (var i = 0; i < collection.length; i++) {
var obj = {
'_id': collection[i]['_id'],
'title': collection[i]['title'],
'parentId': parent ? parent['_id'] : null,
'path': parent ? ',' + parent['_id'] : ''
};
out.push(obj);
if (collection[i].nodes) {
generate(collection[i].nodes, collection[i]);
} else {
generate(collection[i].children, collection[i]);
}
}
};
See example below:
var tree = [
{
'_id': 1,
'title': 'node1',
'nodes': [
{
'_id': 11,
'title': 'node1.1',
'nodes': [
{
'_id': 111,
'title': 'node1.1.1',
'children': [
{
'_id': 1111,
'title': 'node1.1.1.1',
'children': [
]
}
]
}
]
},
{
'_id': 12,
'title': 'node1.2',
'children': [
]
}
]
},
{
'_id': 2,
'title': 'node2',
'children': [
{
'id': 21,
'title': 'node2.1',
'children': [
]
},
{
'_id': 22,
'title': 'node2.2',
'children': [
]
}
]
},
{
'_id': 3,
'title': 'node3',
'children': [
{
'id': 31,
'title': 'node3.1',
'children': [
]
}
]
},
{
'_id': 4,
'title': 'node4',
'children': [
{
'_id': 41,
'title': 'node4.1',
'children': [
]
}
]
}
];
var out = [
];
var generate = function (collection, parent) {
for (var i = 0; i < collection.length; i++) {
var obj = {
'_id': collection[i]['_id'],
'title': collection[i]['title'],
'parentId': parent ? parent['_id'] : null,
'path': parent ? ',' + parent['_id'] : ''
};
out.push(obj);
if (collection[i].nodes) {
generate(collection[i].nodes, collection[i]);
} else {
generate(collection[i].children, collection[i]);
}
}
};
generate(tree, null);
console.log(JSON.stringify(out,null,4));
//prints
[
{
"_id": 1,
"title": "node1",
"parentId": null,
"path": ""
},
{
"_id": 11,
"title": "node1.1",
"parentId": 1,
"path": ",1"
},
{
"_id": 111,
"title": "node1.1.1",
"parentId": 11,
"path": ",11"
},
{
"_id": 1111,
"title": "node1.1.1.1",
"parentId": 111,
"path": ",111"
},
{
"_id": 12,
"title": "node1.2",
"parentId": 1,
"path": ",1"
},
{
"_id": 2,
"title": "node2",
"parentId": null,
"path": ""
},
{
"title": "node2.1",
"parentId": 2,
"path": ",2"
},
{
"_id": 22,
"title": "node2.2",
"parentId": 2,
"path": ",2"
},
{
"_id": 3,
"title": "node3",
"parentId": null,
"path": ""
},
{
"title": "node3.1",
"parentId": 3,
"path": ",3"
},
{
"_id": 4,
"title": "node4",
"parentId": null,
"path": ""
},
{
"_id": 41,
"title": "node4.1",
"parentId": 4,
"path": ",4"
}
]

Categories

Resources