Hierarchical Tree using Unordered array - javascript

I have an unordered array. id is a unique value.parent is id of parent. I need a Hierarchical JSON.
var C=[
{
"id": 57,
"name": "Breaded Chicken Items",
"slug": "breaded-chicken-items",
"parent": 55,
"description": "",
"display": "default",
"image": "http://coitor.com/emke/wp-content/uploads/2016/05/mainbanner-2.jpg",
"count": 0
},
{
"id": 70,
"name": "Curry Masala Blends",
"slug": "curry-masala-blends",
"parent": 69,
"description": "",
"display": "default",
"image": "http://coitor.com/emke/wp-content/uploads/2016/04/purto-probatus.jpg",
"count": 0
},
{
"id": 55,
"name": "Fish | Meat | Frozen Veg",
"slug": "fish-meat-frozen-veg",
"parent": 0,
"description": "",
"display": "default",
"image": "http://coitor.com/emke/wp-content/uploads/2016/05/mainbanner-2.jpg",
"count": 0
},
{
"id": 186,
"name": "Frozen Veg",
"slug": "frozen-veg",
"parent": 55,
"description": "",
"display": "default",
"image": "http://coitor.com/emke/wp-content/uploads/2016/05/mainbanner-1.jpg",
"count": 0
},
{
"id": 69,
"name": "Spices | Curry Powders",
"slug": "spices-curry-powders",
"parent": 0,
"description": "",
"display": "default",
"image": "http://coitor.com/emke/wp-content/uploads/2016/04/rsz_birds_eye_chilli.jpg",
"count": 0
},
{
"id": 47,
"name": "Vegetables",
"slug": "vegetables",
"parent": 0,
"description": "",
"display": "subcategories",
"image": "http://coitor.com/emke/wp-content/uploads/2016/04/wisi-antiopam.jpg",
"count": 15
},
{
"id": 72,
"name": "Whole Spices",
"slug": "whole-spices",
"parent": 69,
"description": "",
"display": "default",
"image": "http://coitor.com/emke/wp-content/uploads/2016/04/fresh-greenpeas.jpg",
"count": 0
}
]
Like this output
[{
"id": 55,
"name": "Fish | Meat | Frozen Veg",
"slug": "fish-meat-frozen-veg",
"parent": 0,
"description": "",
"display": "default",
"image": "http://coitor.com/emke/wp-content/uploads/2016/05/mainbanner-2.jpg",
"count": 0
"Children":[
{
"id": 57,
"name": "Breaded Chicken Items",
"slug": "breaded-chicken-items",
"parent": 55,
"description": "",
"display": "default",
"image": "http://coitor.com/emke/wp-content/uploads/2016/05/mainbanner-2.jpg",
"count": 0
}
]
}]..etc
I have no idea.

You could use a single loop approach by using both information of id and parent for generating nodes in an object.
If a node is found without parent, then the node is a root node and added to the result array.
var data = [{ id: 57, name: "Breaded Chicken Items", slug: "breaded-chicken-items", parent: 55, description: "", display: "default", image: "http://coitor.com/emke/wp-content/uploads/2016/05/mainbanner-2.jpg", count: 0 }, { id: 70, name: "Curry Masala Blends", slug: "curry-masala-blends", parent: 69, description: "", display: "default", image: "http://coitor.com/emke/wp-content/uploads/2016/04/purto-probatus.jpg", count: 0 }, { id: 55, name: "Fish | Meat | Frozen Veg", slug: "fish-meat-frozen-veg", parent: 0, description: "", display: "default", image: "http://coitor.com/emke/wp-content/uploads/2016/05/mainbanner-2.jpg", count: 0 }, { id: 186, name: "Frozen Veg", slug: "frozen-veg", parent: 55, description: "", display: "default", image: "http://coitor.com/emke/wp-content/uploads/2016/05/mainbanner-1.jpg", count: 0 }, { id: 69, name: "Spices | Curry Powders", slug: "spices-curry-powders", parent: 0, description: "", display: "default", image: "http://coitor.com/emke/wp-content/uploads/2016/04/rsz_birds_eye_chilli.jpg", count: 0 }, { id: 47, name: "Vegetables", slug: "vegetables", parent: 0, description: "", display: "subcategories", image: "http://coitor.com/emke/wp-content/uploads/2016/04/wisi-antiopam.jpg", count: 15 }, { id: 72, name: "Whole Spices", slug: "whole-spices", parent: 69, description: "", display: "default", image: "http://coitor.com/emke/wp-content/uploads/2016/04/fresh-greenpeas.jpg", count: 0 }],
tree = function (data, root) {
var r = [], o = {};
data.forEach(function (a) {
a.children = o[a.id] && o[a.id].children;
o[a.id] = a;
if (a.parent === root) {
r.push(a);
return;
}
o[a.parent] = o[a.parent] || {};
o[a.parent].children = o[a.parent].children || [];
o[a.parent].children.push(a);
});
return r;
}(data, 0);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

loop through the array using a looping function. then filter the values by its id. something like this
var col;
var result = C.forEach(obj => {
col = C.filter(item => {
return item.parent == obj.id
})
if (col.length) {
obj['children'] = col;
}
})

Related

Recursive Function in TypeScript - Parents Array

I want to create a recursive function that receive a List of objects that contains the id and parent_id. If the parent of an element is in the list I want to remove it and add it to the parent.
Convert this:
{
"id": 180,
"children": [],
"parent_id": 195,
"name": "Object 180"
},
{
"id": 193,
"children": [],
"parent_id": 180,
"name": "Object 193"
},
{
"id": 194,
"children": [],
"parent_id": 180,
"name": "Object 194"
}
{
"id": 199,
"children": [],
"parent_id": 187,
"name": "Object 199"
}
{
"id": 304,
"children": [],
"parent_id": 193,
"name": "Object 304"
}
To this:
{
"id": 180,
"children": [
{
"id": 193,
"children": [
{
"id": 304,
"children": [],
"parent_id": 193,
"name": "Object 304"
}
],
"parent_id": 180,
"name": "Object 193"
},
{
"id": 194,
"children": [],
"parent_id": 180,
"name": "Object 194"
}
],
"parent_id": 195,
"name": "Object 180"
},
{
"id": 199,
"children": [],
"parent_id": 187,
"name": "Object 199"
}
Sometimes the parent_id is null, and there is no limit of levels of the parents.
Since basarat's answer does not account for items nested more than one level.
Here a solution that creates an output with arbitrary nesting-depth:
const listToTree = (input) => {
const map = new Map(input.map((item) => [item.id, item]));
const output = [];
for (const item of input) {
if (map.has(item.parent_id)) {
map.get(item.parent_id).children.push(map.get(item.id));
} else {
output.push(map.get(item.id));
}
}
return output;
};
const input = [
{
"id": 180,
"value": 10,
"children": [],
"parent_id": 195,
"name": "Object 180"
},
{
"id": 193,
"value": 10,
"children": [],
"parent_id": 180,
"name": "Object 193"
},
{
"id": 194,
"value": 10,
"children": [],
"parent_id": 180,
"name": "Object 194"
},
{
"id": 199,
"children": [],
"parent_id": 187,
"name": "Object 199"
},
{
"id": 304,
"value": 10,
"children": [],
"parent_id": 193,
"name": "Object 304"
},
{
"id": 305,
"value": 10,
"children": [],
"parent_id": 194,
"name": "Object 304"
}
];
const output = listToTree(input);
console.log(output);
Edit: Aggregate values
If you want to aggregate values along ancestry chains of the resulting tree I would recommend to do this in a separate function afterwards. This will keep your code cleaner, easier to test and more readable.
The implementation depends on whether or not your input-array is sorted (children before parents). If you want to process unordered inputs you have to loop through the each ancestry chain.
function aggregateValue(branch) {
const children = branch.children || [];
return children.reduce((sum, child) => sum + aggregateValue(child), branch.value || 0);
}
function aggregateValueAlongBranches(tree) {
return tree.map((branch) => {
return {
...branch,
aggregatedValue: aggregateValue(branch),
children: aggregateValueAlongBranches(branch.children),
};
});
}
const input = [
{
"id": 180,
"value": 10,
"children": [
{
"id": 193,
"value": 10,
"children": [
{
"id": 304,
"value": 10,
"children": [],
"parent_id": 193,
"name": "Object 304"
}
],
"parent_id": 180,
"name": "Object 193"
},
{
"id": 194,
"value": 10,
"children": [
{
"id": 305,
"value": 10,
"children": [],
"parent_id": 194,
"name": "Object 304"
}
],
"parent_id": 180,
"name": "Object 194"
}
],
"parent_id": 195,
"name": "Object 180"
},
{
"id": 199,
"children": [],
"parent_id": 187,
"name": "Object 199"
}
];
const output = aggregateValueAlongBranches(input);
console.log(output);
You don't need a recursive function. Just track items you've already seen and if parent exists in it add to parent.children or add a new root node.
An example complete solution is attached.
Complete code
type Item = {
id: number,
children: Item[],
parent_id: number,
name: string,
}
const items: Item[] = [
{
"id": 180,
"children": [],
"parent_id": 195,
"name": "Object 180"
},
{
"id": 193,
"children": [],
"parent_id": 180,
"name": "Object 193"
},
{
"id": 194,
"children": [],
"parent_id": 180,
"name": "Object 194"
},
{
"id": 199,
"children": [],
"parent_id": 187,
"name": "Object 199"
},
{
"id": 304,
"children": [],
"parent_id": 193,
"name": "Object 304"
}
];
function nest(items:Item[]): Item[] {
const output: Item[] = [];
const idToItem = new Map<number,Item>();
for (let item of items) {
// Either add to parent. Or create a new root level node
if (idToItem.has(item.parent_id)) {
idToItem.get(item.parent_id)!.children.push(item);
} else {
idToItem.set(item.id, item);
output.push(item);
}
}
return output;
}
console.log(nest(items));

Using angularjs forEach loops

I am getting this type of json in my $scope of angularjs:
$scope.someStuff = {
"id": 2,
"service": "bike",
"min": "22",
"per": "100",
"tax": "1",
"categoryservices": [
{
"id": 32,
"category": {
"id": 1,
"name": "software"
}
},
{
"id": 33,
"category": {
"id": 2,
"name": "hardware"
}
},
{
"id": 34,
"category": {
"id": 3,
"name": "waterwash"
}
}
]
}
I want to use angularjs forEach loop and i want to get only category name,
My expected output:
[{"name":"software"}, {"name":"hardware"}, {"name":"waterwash"}]
You can use Array.map()
The map() method creates a new array with the results of calling a provided function on every element in the calling array.
$scope.someStuff.categoryservices.map((x) => { return { name: x.category.name}})
var obj = {
"id": 2,
"service": "bike",
"min": "22",
"per": "100",
"tax": "1",
"categoryservices": [{
"id": 32,
"category": {
"id": 1,
"name": "software"
}
},
{
"id": 33,
"category": {
"id": 2,
"name": "hardware"
}
},
{
"id": 34,
"category": {
"id": 3,
"name": "waterwash"
}
}
]
};
console.log(obj.categoryservices.map((x) => {
return {
name: x.category.name
}
}))
You can use map method by passing a callback function as parameter.
const someStuff = { "id": 2, "service": "bike", "min": "22", "per": "100", "tax": "1", "categoryservices": [ { "id": 32, "category": { "id": 1, "name": "software" } }, { "id": 33, "category": { "id": 2, "name": "hardware" } }, { "id": 34, "category": { "id": 3, "name": "waterwash" } } ] }
let array = someStuff.categoryservices.map(function({category}){
return {'name' : category.name}
});
console.log(array);

How to find all unique paths in tree structure

root1
child1
child2
grandchild1
grandchild2
child3
root2
child1
child2
grandchild1
greatgrandchild1
I have an object array like tree structure like above, I want to get all unique paths in like this
Food->Dry Food Items->Local Dry Food Items
Food->Dry Food Items->Thai Dry Food Items
Food->Dry Food Items->Others
Food->Fruits
------
------
This is my object
[
{
"id": 1,
"name": "Food",
"parent_id": 0,
"children": [
{
"id": 5,
"name": "Dry Food Items",
"parent_id": 1,
"children": [
{
"id": 11,
"name": "Local Dry Food Items",
"parent_id": 5
},
{
"id": 12,
"name": "Thai Dry Food Items",
"parent_id": 5
},
{
"id": 60,
"name": "Others",
"parent_id": 5
}
]
},
{
"id": 6,
"name": "Fruits",
"parent_id": 1
},
{
"id": 7,
"name": "LG Branded",
"parent_id": 1
},
{
"id": 8,
"name": "Meat",
"parent_id": 1
},
{
"id": 9,
"name": "Sea food",
"parent_id": 1
},
{
"id": 10,
"name": "Vegetables",
"parent_id": 1,
"children": [
{
"id": 14,
"name": "Local Vegetables",
"parent_id": 10
},
{
"id": 15,
"name": "Thai Vegetables",
"parent_id": 10
}
]
},
{
"id": 38,
"name": "Frozen",
"parent_id": 1
},
{
"id": 39,
"name": "IP Kitchen",
"parent_id": 1,
"children": [
{
"id": 40,
"name": "IP Meat",
"parent_id": 39
},
{
"id": 41,
"name": "IP Starter",
"parent_id": 39
},
{
"id": 42,
"name": "IP Ingredients",
"parent_id": 39
},
{
"id": 43,
"name": "IP Sauce",
"parent_id": 39
},
{
"id": 44,
"name": "IP Seafood",
"parent_id": 39
},
{
"id": 45,
"name": "IP Starter",
"parent_id": 39
},
{
"id": 46,
"name": "IP Desert",
"parent_id": 39
}
]
}
]
},
{
"id": 2,
"name": "Beverage",
"parent_id": 0,
"children": [
{
"id": 16,
"name": "Bar",
"parent_id": 2
},
{
"id": 17,
"name": "Coffee & Tea",
"parent_id": 2
},
{
"id": 18,
"name": "In Can",
"parent_id": 2
},
{
"id": 19,
"name": "Water",
"parent_id": 2
},
{
"id": 47,
"name": "IP Bar",
"parent_id": 2
}
]
},
{
"id": 3,
"name": "Disposable",
"parent_id": 0,
"children": [
{
"id": 21,
"name": "Disposable",
"parent_id": 3
}
]
},
{
"id": 4,
"name": "SOE",
"parent_id": 0,
"children": [
{
"id": 20,
"name": "Cleaning Materials",
"parent_id": 4
},
{
"id": 22,
"name": "Chinaware",
"parent_id": 4
}
]
}
];
I get to all the nodes in the tree
function traverse(categories) {
categories.forEach(function (category) {
if (category.children && category.children.length) {
traverse(category.children);
}
else {
}
}, this);
}
You can use recursion and create a function using forEach loop.
var arr = [{"id":1,"name":"Food","parent_id":0,"children":[{"id":5,"name":"Dry Food Items","parent_id":1,"children":[{"id":11,"name":"Local Dry Food Items","parent_id":5},{"id":12,"name":"Thai Dry Food Items","parent_id":5},{"id":60,"name":"Others","parent_id":5}]},{"id":6,"name":"Fruits","parent_id":1},{"id":7,"name":"LG Branded","parent_id":1},{"id":8,"name":"Meat","parent_id":1},{"id":9,"name":"Sea food","parent_id":1},{"id":10,"name":"Vegetables","parent_id":1,"children":[{"id":14,"name":"Local Vegetables","parent_id":10},{"id":15,"name":"Thai Vegetables","parent_id":10}]},{"id":38,"name":"Frozen","parent_id":1},{"id":39,"name":"IP Kitchen","parent_id":1,"children":[{"id":40,"name":"IP Meat","parent_id":39},{"id":41,"name":"IP Starter","parent_id":39},{"id":42,"name":"IP Ingredients","parent_id":39},{"id":43,"name":"IP Sauce","parent_id":39},{"id":44,"name":"IP Seafood","parent_id":39},{"id":45,"name":"IP Starter","parent_id":39},{"id":46,"name":"IP Desert","parent_id":39}]}]},{"id":2,"name":"Beverage","parent_id":0,"children":[{"id":16,"name":"Bar","parent_id":2},{"id":17,"name":"Coffee & Tea","parent_id":2},{"id":18,"name":"In Can","parent_id":2},{"id":19,"name":"Water","parent_id":2},{"id":47,"name":"IP Bar","parent_id":2}]},{"id":3,"name":"Disposable","parent_id":0,"children":[{"id":21,"name":"Disposable","parent_id":3}]},{"id":4,"name":"SOE","parent_id":0,"children":[{"id":20,"name":"Cleaning Materials","parent_id":4},{"id":22,"name":"Chinaware","parent_id":4}]}]
function getNames(data) {
var result = [];
function loop(data, c) {
data.forEach(function (e) {
var name = !c.length ? e.name : c + '->' + e.name;
if (e.children) { loop(e.children, name); }
else {
result.push({ name: name });
}
});
}
loop(data, '');
return result;
}
console.log(getNames(arr))

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