Flatten a JavaScript tree, tried some solutions - javascript
Given this tree, is there an easy way to convert it to a flat array with the following conditions?
JS ES5, jQuery used too
respecting the order of presentation
add a "level" property that shows the level of indentation, starting from 0
[
{
"root": "0",
"id": "1",
"name": "Frutta",
"status": "1",
"position": "1",
"children": [
{
"root": "1",
"id": "4",
"name": "Agrumi",
"status": "1",
"position": "3",
"children": []
},
{
"root": "1",
"id": "5",
"name": "Pere",
"status": "1",
"position": "4",
"children": []
}
]
},
{
"root": "0",
"id": "2",
"name": "Ortaggi",
"status": "1",
"position": "1",
"children": [
{
"root": "2",
"id": "7",
"name": "Da foglia",
"status": "1",
"position": "6",
"children": [
{
"root": "7",
"id": "9",
"name": "Insalate",
"status": "1",
"position": "1",
"children": []
}
]
},
{
"root": "2",
"id": "8",
"name": "Da fiore",
"status": "1",
"position": "7",
"children": []
},
{
"root": "2",
"id": "21",
"name": "Da frutto",
"status": "1",
"position": "16",
"children": []
},
{
"root": "2",
"id": "22",
"name": "Da radici",
"status": "1",
"position": "17",
"children": []
},
{
"root": "2",
"id": "23",
"name": "Da fusto",
"status": "1",
"position": "8",
"children": []
},
{
"root": "2",
"id": "24",
"name": "Da bulbi",
"status": "1",
"position": "18",
"children": []
}
]
},
{
"root": "0",
"id": "15",
"name": "Colori",
"status": "1",
"position": "14",
"children": [
{
"root": "15",
"id": "3",
"name": "Banane",
"status": "1",
"position": "2",
"children": [
{
"root": "3",
"id": "6",
"name": "Mele",
"status": "1",
"position": "5",
"children": []
}
]
},
{
"root": "15",
"id": "16",
"name": "Blu/Viola",
"status": "1",
"position": "10",
"children": []
},
{
"root": "15",
"id": "17",
"name": "Verde",
"status": "1",
"position": "11",
"children": []
},
{
"root": "15",
"id": "18",
"name": "Bianco",
"status": "1",
"position": "12",
"children": []
},
{
"root": "15",
"id": "19",
"name": "Giallo/Arancio",
"status": "1",
"position": "13",
"children": []
},
{
"root": "15",
"id": "20",
"name": "Rosso",
"status": "1",
"position": "14",
"children": []
}
]
},
{
"root": "0",
"id": "25",
"name": "Persone",
"status": "1",
"position": "24",
"children": [
{
"root": "25",
"id": "26",
"name": "Fabrizio",
"status": "1",
"position": "2",
"children": [
{
"root": "26",
"id": "27",
"name": "Uomo",
"status": "1",
"position": "21",
"children": []
}
]
},
{
"root": "25",
"id": "28",
"name": "Ivan",
"status": "1",
"position": "1",
"children": []
}
]
},
{
"root": "0",
"id": "29",
"name": "Category",
"status": "1",
"position": "25",
"children": []
}
]
The result expected should be this:
[
{
"root": "0",
"id": "1",
"name": "Frutta",
"status": "1",
"position": "1"
"level": 0
},
{
"root": "1",
"id": "4",
"name": "Agrumi",
"status": "1",
"position": "3",
"level": 1
},
{
"root": "1",
"id": "5",
"name": "Pere",
"status": "1",
"position": "4",
"level": 1
},
{
"root": "0",
"id": "2",
"name": "Ortaggi",
"status": "1",
"position": "1",
"level": 0
},
{
"root": "2",
"id": "7",
"name": "Da foglia",
"status": "1",
"position": "6",
"level": 1
},
{
"root": "7",
"id": "9",
"name": "Insalate",
"status": "1",
"position": "1",
"level": 2
},
...
I found this function, but has some disadvantages:
var flattenTree = function(treeObj, idAttr, parentAttr, childrenAttr, levelAttr) {
if (!idAttr) idAttr = 'id';
if (!parentAttr) parentAttr = 'root';
if (!childrenAttr) childrenAttr = 'children';
if (!levelAttr) levelAttr = 'level';
function flattenChild(childObj, parentId, level) {
var array = [];
var childCopy = $.extend({}, childObj);
childCopy[levelAttr] = level;
childCopy[parentAttr] = parentId || "0";
delete childCopy[childrenAttr];
array.push(childCopy);
array = array.concat(processChildren(childObj, level));
return array;
};
function processChildren(obj, level) {
if (!level) level = 0;
var array = [];
obj[childrenAttr].forEach(function(childObj) {
array = array.concat(flattenChild(childObj, obj[idAttr], level+1));
});
return array;
};
var result = processChildren(treeObj);
return result;
};
I should build first another array starting with "children" as root
[
"children": <my original array here>
]
the levels start from 1, not 0 (I tried putting level = -1 with no luck)
Here is a working fiddle: https://jsfiddle.net/yLke452z/
Thank you
You could take a recursive method for Array#flatMap and store the level for the next call.
const
flatTree = (level = 0) => ({ children = [], ...object }) => [
{ ...object, level }, ...children.flatMap(flatTree(level + 1))
];
var tree = [{ root: "0", id: "1", name: "Frutta", status: "1", position: "1", children: [{ root: "1", id: "4", name: "Agrumi", status: "1", position: "3", children: [] }, { root: "1", id: "5", name: "Pere", status: "1", position: "4", children: [] }] }, { root: "0", id: "2", name: "Ortaggi", status: "1", position: "1", children: [{ root: "2", id: "7", name: "Da foglia", status: "1", position: "6", children: [{ root: "7", id: "9", name: "Insalate", status: "1", position: "1", children: [] }] }, { root: "2", id: "8", name: "Da fiore", status: "1", position: "7", children: [] }, { root: "2", id: "21", name: "Da frutto", status: "1", position: "16", children: [] }, { root: "2", id: "22", name: "Da radici", status: "1", position: "17", children: [] }, { root: "2", id: "23", name: "Da fusto", status: "1", position: "8", children: [] }, { root: "2", id: "24", name: "Da bulbi", status: "1", position: "18", children: [] }] }, { root: "0", id: "15", name: "Colori", status: "1", position: "14", children: [{ root: "15", id: "3", name: "Banane", status: "1", position: "2", children: [{ root: "3", id: "6", name: "Mele", status: "1", position: "5", children: [] }] }, { root: "15", id: "16", name: "Blu/Viola", status: "1", position: "10", children: [] }, { root: "15", id: "17", name: "Verde", status: "1", position: "11", children: [] }, { root: "15", id: "18", name: "Bianco", status: "1", position: "12", children: [] }, { root: "15", id: "19", name: "Giallo/Arancio", status: "1", position: "13", children: [] }, { root: "15", id: "20", name: "Rosso", status: "1", position: "14", children: [] }] }, { root: "0", id: "25", name: "Persone", status: "1", position: "24", children: [{ root: "25", id: "26", name: "Fabrizio", status: "1", position: "2", children: [{ root: "26", id: "27", name: "Uomo", status: "1", position: "21", children: [] }] }, { root: "25", id: "28", name: "Ivan", status: "1", position: "1", children: [] }] }, { root: "0", id: "29", name: "Category", status: "1", position: "25", children: [] }],
flat = tree.flatMap(flatTree());
console.log(flat);
.as-console-wrapper { max-height: 100% !important; top: 0; }
ES5 with Array#reduce.
function flatTree(level) {
return function(result, object) {
var children = object.children || [],
item = Object.keys(object).reduce(function (o, k) {
if (k !== 'children') o[k] = object[k];
return o;
}, {});
level = level || 0;
item.level = level;
return result.concat(item, children.reduce(flatTree(level + 1), []));
};
}
var tree = [{ root: "0", id: "1", name: "Frutta", status: "1", position: "1", children: [{ root: "1", id: "4", name: "Agrumi", status: "1", position: "3", children: [] }, { root: "1", id: "5", name: "Pere", status: "1", position: "4", children: [] }] }, { root: "0", id: "2", name: "Ortaggi", status: "1", position: "1", children: [{ root: "2", id: "7", name: "Da foglia", status: "1", position: "6", children: [{ root: "7", id: "9", name: "Insalate", status: "1", position: "1", children: [] }] }, { root: "2", id: "8", name: "Da fiore", status: "1", position: "7", children: [] }, { root: "2", id: "21", name: "Da frutto", status: "1", position: "16", children: [] }, { root: "2", id: "22", name: "Da radici", status: "1", position: "17", children: [] }, { root: "2", id: "23", name: "Da fusto", status: "1", position: "8", children: [] }, { root: "2", id: "24", name: "Da bulbi", status: "1", position: "18", children: [] }] }, { root: "0", id: "15", name: "Colori", status: "1", position: "14", children: [{ root: "15", id: "3", name: "Banane", status: "1", position: "2", children: [{ root: "3", id: "6", name: "Mele", status: "1", position: "5", children: [] }] }, { root: "15", id: "16", name: "Blu/Viola", status: "1", position: "10", children: [] }, { root: "15", id: "17", name: "Verde", status: "1", position: "11", children: [] }, { root: "15", id: "18", name: "Bianco", status: "1", position: "12", children: [] }, { root: "15", id: "19", name: "Giallo/Arancio", status: "1", position: "13", children: [] }, { root: "15", id: "20", name: "Rosso", status: "1", position: "14", children: [] }] }, { root: "0", id: "25", name: "Persone", status: "1", position: "24", children: [{ root: "25", id: "26", name: "Fabrizio", status: "1", position: "2", children: [{ root: "26", id: "27", name: "Uomo", status: "1", position: "21", children: [] }] }, { root: "25", id: "28", name: "Ivan", status: "1", position: "1", children: [] }] }, { root: "0", id: "29", name: "Category", status: "1", position: "25", children: [] }],
flat = tree.reduce(flatTree(), []);
console.log(flat);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
Take a JSON in JS and create the nested children based on two keys
I am taking a flat JSON file and trying to create a structured tree which places the relevant children, whose ParentID matches a NodeID, underneath the relevant NodeID. However, if there is no parentID, it should look at the previousSiblingId and place the record underneath the relevant NodeID. I believe I am close, I am able to make it work on the parentId but when I introduce the previousSiblingId it stops working. This is the initial flat file: The expected result should be: [ { "nodeId": "3", "name": "Three", "parentId": null, "previousSiblingId": null, "children": [] }, { "nodeId": "1", "name": "One", "parentId": null, "previousSiblingId": "3", "children": [ { "nodeId": "2", "name": "Two", "parentId": "1", "previousSiblingId": null, "children": [ { "nodeId": "6", "name": "Six", "parentId": "2", "previousSiblingId": null, "children": [] }, { "nodeId": "4", "name": "Four", "parentId": "2", "previousSiblingId": "6", "children": [ { "nodeId": "5", "name": "Five", "parentId": "4", "previousSiblingId": null, "children": [] } ] } ] } ] }, { "nodeId": "7", "name": "Seven", "parentId": null, "previousSiblingId": "1", "children": [ { "nodeId": "8", "name": "Eight", "parentId": "7", "previousSiblingId": null, "children": [] } ] } ] my current result is: [ { "nodeId": "3", "name": "Three", "parentId": null, "previousSiblingId": null, "children": [] }, { "nodeId": "7", "name": "Seven", "parentId": null, "previousSiblingId": "1", "children": [ { "nodeId": "8", "name": "Eight", "parentId": "7", "previousSiblingId": null, "children": [] } ] }, { "nodeId": "1", "name": "One", "parentId": null, "previousSiblingId": "3", "children": [ { "nodeId": "2", "name": "Two", "parentId": "1", "previousSiblingId": null, "children": [ { "nodeId": "4", "name": "Four", "parentId": "2", "previousSiblingId": "6", "children": [ { "nodeId": "5", "name": "Five", "parentId": "4", "previousSiblingId": null, "children": [] } ] }, { "nodeId": "6", "name": "Six", "parentId": "2", "previousSiblingId": null, "children": [] } ] } ] } ] What have I missed? <html> <pre id="json"></pre> <script type="text/javascript"> const data = [{ "nodeId": "4", "name": "Four", "parentId": "2", "previousSiblingId": "6" }, { "nodeId": "8", "name": "Eight", "parentId": "7", "previousSiblingId": null }, { "nodeId": "2", "name": "Two", "parentId": "1", "previousSiblingId": null }, { "nodeId": "6", "name": "Six", "parentId": "2", "previousSiblingId": null }, { "nodeId": "3", "name": "Three", "parentId": null, "previousSiblingId": null }, { "nodeId": "5", "name": "Five", "parentId": "4", "previousSiblingId": null }, { "nodeId": "7", "name": "Seven", "parentId": null, "previousSiblingId": "1" }, { "nodeId": "1", "name": "One", "parentId": null, "previousSiblingId": "3" } ]; const getParentDeep = (arr, targetId) => arr.find(({ nodeId }) => nodeId === targetId) ?? arr.flatMap(({ children }) => getParentDeep(children, targetId)) .filter(e => e) .at(0); const result = data .sort(({ parentId: a }, { parentId: b }) => a - b, ({ previousSiblingId: c }, { previousSiblingId: d }) => c - d) .reduce((acc, { nodeId, name, parentId, previousSiblingId }) => { const obj = { nodeId, name: name, parentId: parentId, previousSiblingId: previousSiblingId, children: [] }; const parentObj = getParentDeep(acc, parentId); const previousSiblingObj = getParentDeep(acc, previousSiblingId); if (parentObj) parentObj.children.push(obj) else if (previousSiblingObj) previousSiblingObj.children.push(obj); else acc.push(obj); return acc; }, []); // console.log(result); //Output the new JSON to the screen document.getElementById("json").textContent = JSON.stringify(result, undefined, 2); </script> </html>
You could take a single loop approach with an object to keep all references with part to children and children to parent. At the end take only the children which have null value as parent. If child contains more than two items sort this array. const data = [{ nodeId: "4", name: "Four", parentId: "2", previousSiblingId: "6" }, { nodeId: "8", name: "Eight", parentId: "7", previousSiblingId: null }, { nodeId: "2", name: "Two", parentId: "1", previousSiblingId: null }, { nodeId: "6", name: "Six", parentId: "2", previousSiblingId: null }, { nodeId: "3", name: "Three", parentId: null, previousSiblingId: null }, { nodeId: "5", name: "Five", parentId: "4", previousSiblingId: null }, { nodeId: "7", name: "Seven", parentId: null, previousSiblingId: "1" }, { nodeId: "1", name: "One", parentId: null, previousSiblingId: "3" }], tree = function (data, root) { const sort = array => { if (array.length < 2) return; let node = array.find(o => o.previousSiblingId === null)?.nodeId, i = 0; while (i < array.length) { if (array[i].nodeId !== node) { const j = array.findIndex(o => o.nodeId === node); array.splice(i, 0, ...array.splice(j, 1)); } node = next[node]; i++; } }, empty = { nodeId: undefined, name: undefined, parentId: undefined, previousSiblingId: undefined }, next = {}, t = {}; data.forEach(o => { Object.assign(t[o.nodeId] = t[o.nodeId] || { ...empty }, o); t[o.parentId] ??= { ...empty }; t[o.parentId].children ??= []; t[o.parentId].children.push(t[o.nodeId]); if (o.previousSiblingId !== null) next[o.previousSiblingId] = o.nodeId; sort(t[o.parentId].children); }); return t[root].children; }(data, null); console.log(tree); .as-console-wrapper { max-height: 100% !important; top: 0; }
Remove an associate object while looping through using map
I have a json array and i need to delete the subarray whose id value is 5, which is falling under the serialNo 1. I tried the following method, but its not deleting any entry in the subarray. let Details = [ { "serialNo": "1", "text": "AAA", "subArray": [{ "id": "1", "name": "geo" }, { "id": "5", "name": "gau" }, { "id": "4", "name": "joi" }] }, { "serialNo": "2", "text": "BBB", "subArray": [{ "id": "7", "name": "rom" }, { "id": "5", "name": "dom" }, { "id": "4", "name": "noi" }] }, { "serialNo": "3", "text": "CCC", "subArray": [{ "id": "1", "name": "glo" }, { "id": "5", "name": "gum" }, { "id": "4", "name": "lom" }] } ]; Details.map((data) => { if (data.serialNo === "1") { data.subArray.map((subDetails) => { if (subDetails.id === "5") { delete data.subArray[subDetails]; } }) } })
I don't know why you explicitely wants to use the map function. But the following works: let Details = [ { "serialNo": "1", "text": "AAA", "subArray": [{ "id": "1", "name": "geo" }, { "id": "5", "name": "gau" }, { "id": "4", "name": "joi" }] }, { "serialNo": "2", "text": "BBB", "subArray": [{ "id": "7", "name": "rom" }, { "id": "5", "name": "dom" }, { "id": "4", "name": "noi" }] }, { "serialNo": "3", "text": "CCC", "subArray": [{ "id": "1", "name": "glo" }, { "id": "5", "name": "gum" }, { "id": "4", "name": "lom" }] } ]; Details = Details.map(function (data) { if (data.serialNo === "1") { data.subArray = data.subArray.filter(function (sa) { return (sa.id !== "5"); }); } return data; }); console.log(Details);
The first problem is that you're not returning anything from the map functions. The second problem is that data.subArray[subDetails] is undefined, subDetails is an object not an index in the data.subArray array. You can use a combination of map and filter to accomplished this instead of using delete. let Details = [ { "serialNo": "1", "text": "AAA", "subArray": [{ "id": "1", "name": "geo" }, { "id": "5", "name": "gau" }, { "id": "4", "name": "joi" }] }, { "serialNo": "2", "text": "BBB", "subArray": [{ "id": "7", "name": "rom" }, { "id": "5", "name": "dom" }, { "id": "4", "name": "noi" }] }, { "serialNo": "3", "text": "CCC", "subArray": [{ "id": "1", "name": "glo" }, { "id": "5", "name": "gum" }, { "id": "4", "name": "lom" }] } ]; Details.map((data) => { if (data.serialNo === "1") { data.subArray = data.subArray.filter((subDetails) => { return subDetails.id !== "5"; }) } return data; }); console.log(Details); If you want to stick with map what you need to do is to return undefined when subDetails.id is 5. let Details = [ { "serialNo": "1", "text": "AAA", "subArray": [{ "id": "1", "name": "geo" }, { "id": "5", "name": "gau" }, { "id": "4", "name": "joi" }] }, { "serialNo": "2", "text": "BBB", "subArray": [{ "id": "7", "name": "rom" }, { "id": "5", "name": "dom" }, { "id": "4", "name": "noi" }] }, { "serialNo": "3", "text": "CCC", "subArray": [{ "id": "1", "name": "glo" }, { "id": "5", "name": "gum" }, { "id": "4", "name": "lom" }] } ]; Details.map((data) => { if (data.serialNo === "1") { data.subArray = data.subArray.filter((subDetails) => { return subDetails.id === "5" ? undefined : subDetails; }) } return data; }); console.log(Details);
One map plus object constructor: const arr = [ { "serialNo": "1", "text": "AAA", "subArray": [{ "id": "1", "name": "geo" }, { "id": "5", "name": "gau" }, { "id": "4", "name": "joi" }] }, { "serialNo": "2", "text": "BBB", "subArray": [{ "id": "7", "name": "rom" }, { "id": "5", "name": "dom" }, { "id": "4", "name": "noi" }] }, { "serialNo": "3", "text": "CCC", "subArray": [{ "id": "1", "name": "glo" }, { "id": "5", "name": "gum" }, { "id": "4", "name": "lom" }] } ]; const s = 1, id = 5; // conditions const r = arr.map(e => (e.serialNo == s) ? Object.assign(e, {'subArray': e.subArray.filter(a => a.id != id)}) : e); console.log(JSON.stringify(r, null, 2)); Object.assign swaps old subArray with the new filtered one.
Deep Flatten JavaScript Object Recursively
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
How to push data into an object?
I have object and I am trying to push data into it, but I am getting the error "push is not a function". Here's my object: var item = { "_id": { "$oid": "asdf976" }, "Categories": [{ "mainmodels": [{ "submodels": [{ "price": "2000", "submodelname": "lumia021", "Remainingphones": "0", "Bookedphones": "0", "Numofphones": "10" }, { "price": "2000", "submodelname": "lumia341", "Remainingphones": "5", "Bookedphones": "5", "Numofphones": "10" } ], "Status": "Active", "modelname": "lumia", "fromdate": "2016-04-01T16:39:12.051Z", "todate": "2016-04-31T19:19:44.051Z" }], "brand": "nokia" }], "rank": "1", "name": "first" } I want to push: var modal = { 'custid': '1', 'packcode': '22' }; item.push(modal); console.log(item); Here's my expected result: var item = { "_id": { "$oid": "asdf976" }, "Categories": [{ "mainmodels": [{ "submodels": [{ "price": "2000", "submodelname": "lumia021", "Remainingphones": "0", "Bookedphones": "0", "Numofphones": "10" }, { "price": "2000", "submodelname": "lumia341", "Remainingphones": "5", "Bookedphones": "5", "Numofphones": "10" } ], "Status": "Active", "modelname": "lumia", "fromdate": "2016-04-01T16:39:12.051Z", "todate": "2016-04-31T19:19:44.051Z" }], "brand": "nokia" }], "rank": "1", "name": "first", 'custid': '1', 'packcode': '22' }; How do I do that?
You don't push into objects, you push into arrays. Based on what you've said you want, you do this: item.custid = '1'; item.packcode = '22'; E.g., just assign properties to the object. In ES2015, you could use Object.assign if having modal be separate is important: var modal = {custid: '1', packcode: '22'}; Object.assign(item, modal); ...but I don't see a need for it here, and note that there's no ongoing link between modal and item. Live Example: var item = { "_id": { "$oid": "asdf976" }, "Categories": [{ "mainmodels": [{ "submodels": [{ "price": "2000", "submodelname": "lumia021", "Remainingphones": "0", "Bookedphones": "0", "Numofphones": "10" }, { "price": "2000", "submodelname": "lumia341", "Remainingphones": "5", "Bookedphones": "5", "Numofphones": "10" } ], "Status": "Active", "modelname": "lumia", "fromdate": "2016-04-01T16:39:12.051Z", "todate": "2016-04-31T19:19:44.051Z" }], "brand": "nokia" }], "rank": "1", "name": "first" }; item.custid = '1'; item.packcode = '22'; // Note: Just using JSON for *display* document.body.innerHTML = "<pre>" + JSON.stringify(item, null, 2) + "</pre>"; Or with Object.assign (requires an up-to-date browser or a shim): var item = { "_id": { "$oid": "asdf976" }, "Categories": [{ "mainmodels": [{ "submodels": [{ "price": "2000", "submodelname": "lumia021", "Remainingphones": "0", "Bookedphones": "0", "Numofphones": "10" }, { "price": "2000", "submodelname": "lumia341", "Remainingphones": "5", "Bookedphones": "5", "Numofphones": "10" } ], "Status": "Active", "modelname": "lumia", "fromdate": "2016-04-01T16:39:12.051Z", "todate": "2016-04-31T19:19:44.051Z" }], "brand": "nokia" }], "rank": "1", "name": "first" }; var modal = { custid: '1', packcode: '22' }; Object.assign(item, modal); // Note: Just using JSON for *display* document.body.innerHTML = "<pre>" + JSON.stringify(item, null, 2) + "</pre>";
Underscore Create array of objects from JSON object
I have following JSON structure: { "shops": { "categories": { "cat_1": { "id": "1", "label": "Men's Fashions", "Brands": [{ "id": "2", "name": "Smith" }] }, "cat_2": { "id": "2", "label": "Restaurants", "Brands": [{ "id": "3", "name": "KFC" }, { "id": "4", "name": "SUBWAY" }, { "id": "5", "name": "MLD" }, { "id": "6", "name": "THAI" }] }, "cat_3": { "id": "3", "label": "Specialty Shops", "Brands": [{ "id": "7", "name": "BODY SHOP" }] } } } } I'd like to achieve something like this: [{ "categoryid": "1", "id": "2", "label": "Men's Fashions", "name": "Smith" }, { "categoryid": "2", "id": "3", "label": "Restaurants", "name": "KFC" }, { "categoryid": "2", "id": "4", "label": "Restaurants", "name": "SUBWAY" }, { "categoryid": "2", "id": "5", "label": "Restaurants", "name": "MLD" }, { "categoryid": "2", "id": "6", "label": "Restaurants", "name": "THAI" }, { "categoryid": "3", "id": "7", "label": "Specialty Shops", "name": "BODY SHOP" }, ] Is there an elegant way to achieve it using underscore? I tried to use nested _.each() to do that, but feel there might be something better. generateArray: function(obj) { var newResult = []; _.each(obj.categories, function(c) { _.each(c.Brands, function(d) { newResult.push({ "categoryid": c.id, "id": d.id, "label": c.label, "name": d.name }); }); }); return newResult; } Anyone can advise me which way is more efficiency at running time? mine or #Artyom Neustroev or #Anthony Chu ?
You don't really need underscore for that task. Use simple for .. in .. and for (...) loops: var json = {...}; var result = []; for (var catKey in json.shops.categories) { var currentCategory = json.shops.categories[catKey]; for (var i = 0; i < currentCategory.Brands.length; i++) { var currentBrand = currentCategory.Brands[i]; result.push({ categoryid: currentCategory.id, label: currentCategory.label, id: currentBrand.id, name: currentBrand.name }); } } Fiddle here
Instead of each()'s, here's a way to do it with map()'s... var output = _.chain(input.shops.categories) .map(function (category) { return _(category.Brands).map(function (brand) { return { categoryId: category.id, id: brand.id, label: category.label, name: brand.name }; }); }).flatten().value(); JSFIDDLE