Build tree array from json in javascript - javascript

I need to build this tree array from this json below,
I still try using filter, map and reduce, but I can't achieve the result.
[{
"code": "2",
"name": "PENDING"
},{
"code": "2.2",
"name": "PENDING CHILDREN"
}, {
"code": "2.2.01.01",
"name": "PENDING CHILDREN CHILDREN"
}, {
"code": "2.2.01.02",
"name": "PENDING CHILDREN CHILDREN02"
}, {
"code": "1",
"name": "ACTIVE"
}, {
"code": "1.1",
"name": "ACTIVE CHILDREN"
}, {
"code": "1.1.01",
"name": "ACTIVE CHILDREN CHILDREN"
}]
but if need build this tree structuring by your code name
[{
"code": "2",
"name": "PENDING",
"children": [{
"code": "2.2",
"name": "PENDING CHILDREN",
"children": [{
"code": "2.2.01.01",
"name": "PENDING CHILDREN CHILDREN"
}, {
"code": "2.2.01.02",
"name": "PENDING CHILDREN CHILDREN02"
}]
}]
},{
"code": "1",
"name": "ACTIVE",
"children": [{
"code": "1.1",
"name": "ACTIVE CHILDREN",
"children": [{
"code": "1.1.01",
"name": "ACTIVE CHILDREN CHILDREN"
}]
}]
}]
I try using reduce, but i dont understand build this logic with javascrtip. Follow my code below
var idToNodeMap = contas.reduce(function(map, node, i) {
map[node.code] = node;
node.children = [];
return map;
});

This may solve your issue
function ensureNode(code, name, root) {
var last;
var node = code.split(/\./g).reduce((prev, cur) => {
last = (last && (last + '.' + cur)) || cur;
if(!prev.children){
prev.children = [];
}
var result = prev.children.find(item => item.code === last);
if(!result) {
prev.children.push(result = {code: last});
}
return result;
}, root);
node.name = name;
}
var data = [{
"code": "2",
"name": "PENDING"
},{
"code": "2.2",
"name": "PENDING CHILDREN"
}, {
"code": "2.2.01.01",
"name": "PENDING CHILDREN CHILDREN"
}, {
"code": "2.2.01.02",
"name": "PENDING CHILDREN CHILDREN02"
}, {
"code": "1",
"name": "ACTIVE"
}, {
"code": "1.1",
"name": "ACTIVE CHILDREN"
}, {
"code": "1.1.01",
"name": "ACTIVE CHILDREN CHILDREN"
}];
var result = {};
data.forEach(item => ensureNode(item.code, item.name, result));
console.log(result);

With sorted data, you could use an object for building the tree and an array for the reference of the parents.
var data = [{ "code": "2", "name": "PENDING" }, { "code": "2.2", "name": "PENDING CHILDREN" }, { "code": "2.2.01.01", "name": "PENDING CHILDREN CHILDREN" }, { "code": "2.2.01.02", "name": "PENDING CHILDREN CHILDREN02" }, { "code": "1", "name": "ACTIVE" }, { "code": "1.1", "name": "ACTIVE CHILDREN" }, { "code": "1.1.01", "name": "ACTIVE CHILDREN CHILDREN" }],
tree = function (data, root) {
var o = {}, last = [root], level = 0;
o[root] = {};
data.forEach(function (a) {
var parent = root;
while (level && last[level].length >= a.code.length) {
level--;
}
parent = last[level];
level++;
last.length = level;
last.push(a.code);
o[a.code] = a;
o[parent].children = o[parent].children || [];
o[parent].children.push(a);
});
return o[root].children;
}(data, '');
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Make object as child according to the Parent Id JavaScript [duplicate]

This question already has answers here:
Build tree array from flat array in javascript
(34 answers)
Building tree array of objects from flat array of objects [duplicate]
(3 answers)
Closed 3 years ago.
I have the following data coming from the API:
[
{
"Code": "01002",
"ParentAccountId": "01",
},
{
"Code": "01001001003",
"ParentAccountId": "01001001",
},
{
"Code": "01001004",
"ParentAccountId": "01001",
},
{
"Code": "02",
"ParentAccountId": null,
},
{
"Code": "01002001",
"ParentAccountId": "01002",
},
{
"Code": "02002",
"ParentAccountId": "02",
},
{
"Code": "02001",
"ParentAccountId": "02",
},
{
"Code": "01001001001",
"ParentAccountId": "01001001",
},
{
"Code": "03",
"ParentAccountId": null,
},
{
"Code": "01002002",
"ParentAccountId": "01002",
},
{
"Code": "03001",
"ParentAccountId": "03",
},
{
"Code": "01",
"ParentAccountId": null,
},
{
"Code": "01001001002",
"ParentAccountId": "01001001",
},
{
"Code": "01001002",
"ParentAccountId": "01001",
},
{
"Code": "01001001",
"ParentAccountId": "01001",
},
{
"Code": "01001003",
"ParentAccountId": "01001",
},
{
"Code": "01001005",
"ParentAccountId": "01001",
},
{
"Code": "01001",
"ParentAccountId": "01",
}
]
Look at the ParentAccountId.
As I need to pass it to the treeview component so, I need to convert it to something like this:
[
{
"Code": "01",
"ParentAccountId": null,
"children": [
{
"Code": "01001",
"ParentAccountId": "01",
"children": [
{
"Code": "01001001",
"ParentAccountId": "01001",
"children": [
{
"Code": "01001001001",
"ParentAccountId": "01001001",
"children": [],
},
{
"Code": "01001001002",
"ParentAccountId": "01001001",
"children": [],
},
{
"Code": "01001001003",
"ParentAccountId": "01001001",
"children": [],
},
],
},
{
"Code": "01001002",
"ParentAccountId": "01001",
"children": [],
},
{
"Code": "01001003",
"ParentAccountId": "01001",
"children": [],
},
{
"Code": "01001004",
"ParentAccountId": "01001",
"children": [],
},
{
"Code": "01001005",
"ParentAccountId": "01001",
"children": [],
}
],
},
{
"Code": "01002",
"ParentAccountId": "01",
"children": [
{
"Code": "01002001",
"ParentAccountId": "01002",
"children": [],
},
{
"Code": "01002002",
"ParentAccountId": "01002",
"children": [],
},
],
},
],
},
{
"Code": "02",
"ParentAccountId": null,
"children": [
{
"Code": "02001",
"ParentAccountId": "02",
"children": [],
},
{
"Code": "02002",
"ParentAccountId": "02",
"children": [],
},
],
},
{
"Code": "03",
"ParentAccountId": null,
"children": [
{
"Code": "03001",
"ParentAccountId": "03",
"children": [],
},
],
},
]
I want to make the the object as child of it's parent according the code. The scheme is if the ParentAccountId is null it's the top level parent, if the ParentAccountId is of length 2 then it's the 1st level child if the ParentAccountId is of length 5 then it's the 3rd level child then if ParentAccountId is of length 8 then it's 4th level child then ParentAccountId is of length 11 then it's 5th level child. As the 1st level child have 2 length of ParentAccountId then the subsequent children will have the ParentAccountId as Code of the parent plus. For better understading please see the second because my English is not that better.
I am confused about the logic. Any Suggestions?
You could create tree structure using reduce method to create recursive function where in each iteration you check if the parent id is equal to current element id.
const data = [{"Id":"1","Code":"01","Title":"Account 01","ParentAccountId":null},{"Id":"2","Code":"02","Title":"Account 02","ParentAccountId":null},{"Id":"3","Code":"01001","Title":"Account 01001","ParentAccountId":"01"},{"Id":"4","Code":"01002","Title":"Account 01002","ParentAccountId":"01"},{"Id":"5","Code":"01002001","Title":"Account 01002001","ParentAccountId":"01002"}]
function toTree(data, pid = null) {
return data.reduce((r, e) => {
if (e.ParentAccountId == pid) {
const obj = { ...e };
const children = toTree(data, e.Code);
if (children.length) obj.children = children;
r.push(obj);
}
return r;
}, [])
}
const result = toTree(data)
console.log(result)
The logic involved is to first try and find the children of every object (Accomplished using filter to find all objects that have a ParentAccountId equal to each objects Code) and then filter the data to return only the root parents (objects with ParentAccountId equal to null).
Try the code below.
var data = [{
"Id": "1",
"Code": "01",
"Title": "Account 01",
"ParentAccountId": null
},
{
"Id": "2",
"Code": "02",
"Title": "Account 02",
"ParentAccountId": null
},
{
"Id": "3",
"Code": "01001",
"Title": "Account 01001",
"ParentAccountId": "01"
},
{
"Id": "4",
"Code": "01002",
"Title": "Account 01002",
"ParentAccountId": "01"
},
{
"Id": "5",
"Code": "01002001",
"Title": "Account 01002001",
"ParentAccountId": "01002"
}
]
rearrangeData = () => {
var newData = []
data.forEach((x) => {
x['children'] = data.filter((y) => {
return y.ParentAccountId === x.Code
})
var parent = data.find((y) => {
return y.Code === x.ParentAccountId
})
if (parent && parent.children) {
parent.children.push(x)
} else if (parent && !parent.children) {
parent['children'] = [x];
} else {
return x
}
newData.push(parent)
})
var parents = newData.filter((x) => {
return x.ParentAccountId === null
})
console.log(parents);
}
rearrangeData()
I'm waiting on a task at work so I figured I'd throw together an implementation of this for you. Not saying it's any better or worse than the solutions in the threads linked - just another implementation:
const data = [{
"Id": "1",
"Code": "01",
"Title": "Account 01",
"ParentAccountId": null
},
{
"Id": "2",
"Code": "02",
"Title": "Account 02",
"ParentAccountId": null
},
{
"Id": "3",
"Code": "01001",
"Title": "Account 01001",
"ParentAccountId": "01"
},
{
"Id": "4",
"Code": "01002",
"Title": "Account 01002",
"ParentAccountId": "01"
},
{
"Id": "5",
"Code": "01002001",
"Title": "Account 01002001",
"ParentAccountId": "01002"
}
]
function buildTree(obj) {
// get all top level parents
let parents = obj.filter((o) => !o.ParentAccountId);
// loop over the parents and recursively call addChild to populate the tree
parents.forEach((p) => {
p.children = addChildren(p, obj);
});
return parents;
}
function addChildren(parent, obj) {
// find all children for this parent
let children = obj.filter((o) => o.ParentAccountId === parent.Code)
if (children.length) {
// loop over any children recursively calling this function to add nested children
children.forEach((c) => {
c.children = addChildren(c, obj);
});
return children;
} else {
return [];
}
}
console.log(buildTree(data));
You can iterate over every node and build a map of child-id to child.
Then, loop over the nodes again, but this time looking at the parent-ids and pushing the node to the parent if it is a child, or adding it to the root list. This response is adapted from here with the addition of a custom key configuration.
I also added a method to remove the children field on any object that does not contain children i.e. leaf nodes.
console.log(convertListToTree(getDataList(), {
idKey : 'Code',
parentIdKey : 'ParentAccountId',
pruneEmptyChildren : true
}));
function convertListToTree(list, options) {
options = Object.assign({
idKey : 'id',
parentIdKey : 'parentId',
childrenKey : 'children',
pruneEmptyChildren : false
}, options || {});
let map = {}, node, roots = [], i;
for (i = 0; i < list.length; i++) {
map[list[i][options.idKey]] = i;
list[i][options.childrenKey] = []; // Attach a "child" reference holder
}
for (i = 0; i < list.length; i++) {
node = list[i];
if (node[options.parentIdKey] != null) {
list[map[node[options.parentIdKey]]][options.childrenKey].push(node);
} else {
roots.push(node);
}
}
if (options.pruneEmptyChildren) {
pruneEmptyKeys(roots, options.childrenKey); // Remove empty
}
return roots;
}
function pruneEmptyKeys(tree, childKey) {
let items = tree[childKey] || tree;
items.forEach(item => {
if (item[childKey].length > 0) {
pruneEmptyKeys(item[childKey], childKey);
} else {
delete item[childKey]; // Remove empty child list
}
});
}
function getDataList() {
return [{
"Id": "1",
"Code": "01",
"Title": "Account 01",
"ParentAccountId": null
}, {
"Id": "2",
"Code": "02",
"Title": "Account 02",
"ParentAccountId": null
}, {
"Id": "3",
"Code": "01001",
"Title": "Account 01001",
"ParentAccountId": "01"
}, {
"Id": "4",
"Code": "01002",
"Title": "Account 01002",
"ParentAccountId": "01"
}, {
"Id": "5",
"Code": "01002001",
"Title": "Account 01002001",
"ParentAccountId": "01002"
}];
}
.as-console-wrapper {
top: 0;
max-height: 100% !important;
}
<!-- Adapted from: https://stackoverflow.com/a/18018037/1762224 -->

Grouping a multilevel array of objects

I am trying to learn javascript reduce and map an I came across some difficulties.
I have an array with the following format. The id of the parent is same as the location_id of the child. I need to group the array into a nested format.
arr = [
{
"id": 4583211,
"name": "Location 1",
"location_id": null,
},
{
"id": 7458894,
"name": "Location 12",
"location_id": 4583211
},
{
"id": 7463953,
"name": "Location 13",
"location_id": 4583211
},
{
"id": 80302210,
"name": "Location 121",
"location_id": 7458894
},
{
"id": 80302219,
"name": "Location 122",
"location_id": 7458894
},
{
"id": 7464314,
"name": "Location 131",
"location_id": 7463953
},
{
"id": 4583216,
"name": "Location 2",
"location_id": null,
},
{
"id": 3566353,
"name": "Location 21",
"location_id": 4583216
},
]
This array should be grouped as:
result = [
{
"id": 4583211,
"name": "Location 1",
"locations": [
{
"id": 7458894,
"name": "Location 12",
"locations": [
{
"id": 80302210,
"name": "Location 121"
},
{
"id": 80302219,
"name": "Location 122"
}
]
},
{
"id": 7463953,
"name": "Location 13",
"locations": [
{
"id": 7464314,
"name": "Location 131"
}
]
}
]
},
{
"id": 4583216,
"name": "Location 2",
"locations": [
{
"id": 3566353,
"name": "Location 21"
}
]
}
]
I tried to group it using the following method found on SO but it gives different result.
result = arr.reduce(function (r, a) {
r[a.location_id] = r[a.location_id] || [];
r[a.location_id].push(a);
return r;
}, Object.create(null));
You could do this using reduce and recursion you just need to check if parent is equal to current elements location_id.
const data = [{"id":4583211,"name":"Location 1","location_id":null},{"id":7458894,"name":"Location 12","location_id":4583211},{"id":7463953,"name":"Location 13","location_id":4583211},{"id":80302210,"name":"Location 121","location_id":7458894},{"id":80302219,"name":"Location 122","location_id":7458894},{"id":7464314,"name":"Location 131","location_id":7463953},{"id":4583216,"name":"Location 2","location_id":null},{"id":3566353,"name":"Location 21","location_id":4583216}]
function create(data, parent = null) {
return data.reduce((r, e) => {
if(parent == e.location_id) {
const o = { id: e.id, name: e.name }
const children = create(data, e.id);
if(children.length) o.locations = children;
r.push(o)
}
return r
}, [])
}
console.log(create(data))

Javascript filter for multidimensional json object

Can't use javascript filter in multi-dimensional object.
var object = [{
"id": "1",
"name": "General",
"cards": [{
"id": "1",
"name": "shawn"
}, {
"id": "2",
"name": "neo"
}]
}, {
"id": "2",
"name": "CEO",
"cards": [{
"id": "1",
"name": "Raman"
}, {
"id": "2",
"name": "Sheena"
}]
}]
function searchFor(item) {
return item.cards.filter(
(card) => {
return card.name.indexOf("Raman") !== -1;
}
);
}
var filtered = object.filter(searchFor);
console.log(filtered);
This is how I am trying, inside the searchFor card.name I am getting the correct card name but filtering is returning all the cards.Its not filtering.
Could any help me with this.
An empty array isn't considered falsey in Javascript. So instead of returning the result of filtering the cards array, test its length.
var object = [{
"id": "1",
"name": "General",
"cards": [{
"id": "1",
"name": "shawn"
}, {
"id": "2",
"name": "neo"
}]
}, {
"id": "2",
"name": "CEO",
"cards": [{
"id": "1",
"name": "Raman"
}, {
"id": "2",
"name": "Sheena"
}]
}]
function searchFor(item) {
return item.cards.filter(
(card) => {
return card.name.indexOf("Raman") !== -1;
}
).length != 0;
}
var filtered = object.filter(searchFor);
console.log(filtered);
You were returning the filtered array, which would produce a TRUE result whenever cards existed. So you can just turn that into a boolean, by saying when the item.cards.filter(...).length > 0.
var object = [{
"id": "1",
"name": "General",
"cards": [{
"id": "1",
"name": "shawn"
}, {
"id": "2",
"name": "neo"
}]
}, {
"id": "2",
"name": "CEO",
"cards": [{
"id": "1",
"name": "Raman"
}, {
"id": "2",
"name": "Sheena"
}]
}]
var searchFor = (card) => card.name.indexOf("Raman") > -1;
var filteredCards = object.reduce((cards, item) => cards.concat(item.cards.filter(searchFor)), []);
var filteredObj = object.map(i => {
i.cards = i.cards.filter(searchFor);
return i;
}).filter(i => i.cards.length)
console.log(filteredCards, filteredObj)
Updated
I updated the code snippet to produce either the cards which were found. I also provide a method for returning all objects which contain the needed cards, and filter out the other cards.
// HTML Part
<div class="filter-list">
<button class="filter" data-filter-key="all">all</button>
<button class="filter" data-filter-key="open">open</button>
<button class="filter" data-filter-key="done">done</button>
</div>
// CSS Part
.filter:hover,
.filter:focus,
[data-active-filter="all"] .filter[data-filter-key="all"],
[data-active-filter="done"] .filter[data-filter-key="done"],
[data-active-filter="open"] .filter[data-filter-key="open"] {
text-decoration: underline;
}
[data-active-filter="open"] [data-completed="true"],
[data-active-filter="done"] [data-completed="false"] {
display: none;
}
// Script Part
(function () {
const mainNode = document.querySelector("main");
const filters = document.querySelector(".filter-list");
for (const filter of filters.children) {
filter.addEventListener("click", () => {
mainNode.setAttribute(
"data-active-filter",
filter.getAttribute("data-filter-key")
);
});
}
mainNode.setAttribute("data-active-filter", "all");
})();

I'm receiving an array of object in the API response and is nested array. i want find a object by ID

Hi Here is an array of object receiving from the API response and i'm using angular.js and javascript and i want get object with find by id and id is "1_1_1".is this possible? i'm stuck on it. please help.
[
{
"text": "folder 1",
"parent": {
"id": "1",
"name": "folder 1"
},
"children": [
{
"text": "folder 1_1",
"parent": {
"id": "1_1",
"name": "folder 1_1"
},
"children": [
{
"text": "folder 1_1_1",
"parent": {
"id": "1_1_1",
"name": "folder 1_1_1"
},
"children": []
},
{
"text": "folder 1_1_2",
"parent": {
"id": "1_1_2",
"name": "folder 1_1_2"
},
"children": []
}
]
},
{
"text": "folder 1_2",
"parent": {
"id": "1_2",
"name": "folder 1_2"
},
"children": [
{
"text": "folder 1_2_1",
"parent": {
"id": "1_2_1",
"name": "folder 1_2_1"
},
"children": []
}
]
}
]
},
{
"text": "folder 2",
"parent": {
"id": "2",
"name": "folder 2"
},
"children": [
{
"text" : "folder 2_1",
"parent": {
"id": "2_1",
"name": "folder 2_1"
},
"children": []
},
{
"text" : "folder 2_2",
"parent": {
"id": "2_2",
"name": "folder 2_2"
},
"children": []
},
{
"text" : "folder 2_3",
"parent": {
"id": "2_3",
"name": "folder 2_3"
},
"children": [
{
"text" : "folder 2_3_1",
"parent": {
"id": "2_3_1",
"name": "folder 2_3_1"
},
"children": []
}
]
},
]
},
{
"text": "folder 3",
"parent": {
"id": "3",
"name": "folder 3"
},
"children": []
}
]
Try this:
function isMatch(element, criteria) {
var parent = null;
if (typeof element.parent !== 'undefined') {
parent = element.parent;
if (typeof parent.id !== 'undefined') {
return (parent.id === criteria);
}
}
return false;
}
function findById (tree, criteria) {
for (var i = 0; i < tree.length; i = i + 1) {
var obj = tree[i];
if (isMatch(obj, criteria)) {
return obj;
};
if (typeof obj.children !== 'undefined') {
var foundElement = findById(obj.children, criteria);
if (foundElement != null) {
return foundElement;
}
}
}
return null;
}
There is no built in function in either JavaScript or Angular to do that.
However this is a problem that can be solved with recursion quite easily:
function findById(id, items) {
for (var i = 0; i < items.length; i++) {
if (items[i].id === id) {
return items[i];
}
var result = findById(id, items[i].children);
if (result !== null) {
return result;
}
}
return null;
}

Jquery : transform nested json object to another json object

In javascript/jquery how do i achieve following
old_dataset = [
{
"dob": "xyz",
"name": {
"first": " abc",
"last": "lastname"
},
"start_date": {
"moth": "2",
"day": "5",
"year": 1
},
"children": [
{
"child": {
"id": "1",
"desc": "first child"
}
},
{
"child": {
"id": "2",
"desc": "second child"
}
}
]
},
{
"dob": "er",
"name": {
"first": " abc",
"last": "txt"
},
"start_date": {
"moth": "2",
"day": "5",
"year": 1
},
"children": [
{
"child": {
"id": "1",
"desc": "first child"
}
},
{
"child": {
"id": "2",
"desc": "second child"
}
}
]
}
]
Using jquery iterate over the above and change to following
new_dataset = [
{
"dob":"xyz",
"name": <first and last name values>
"start_date":<value of month day year>,
"children": [ {
child_id :1,
child_id : 2
},
]
},{
"dob":"er",
"name": <first and last name values>
"start_date":<value of month day year>,
"children": [ {
child_id :1,
child_id : 2
},
]
}]
If someone can give the code to transform the data it would help me to understand the iteration
You could do something like:
function transformDataset(oldDataset) {
var newDataset = [];
var newObj;
for (var i = 0; i < oldDataset.length; i++) {
newObj = transformObj(oldDataset[i]);
newDataset.push(newObj);
}
return newDataset;
}
function transformObj(obj) {
var children = obj.children;
obj.name = obj.name.first + ' ' + obj.name.last;
obj.start_date = obj.start_date.month + ' ' + obj.start_date.day + ' ' + obj.start_date.year;
obj.children = [];
for (var i = 0; i < children.length; i++) {
obj.children.push(children[i].child.id);
}
return obj;
}
var new_dataset = transformDataset(old_dataset);
Note that new_dataset will have an array of child id instead of an object with multiple child_id properties.
You also had a typo in old_dataset.start_date.month (was written moth)(or maybe that was intentional).
use map first to iterate the array data (old_dataset), replace element name & start_date with new value then return the array
const old_dataset = [
{
"dob": "xyz",
"name": {
"first": " abc",
"last": "lastname"
},
"start_date": {
"moth": "2",
"day": "5",
"year": 1
},
"children": [
{
"child": {
"id": "1",
"desc": "first child"
}
},
{
"child": {
"id": "2",
"desc": "second child"
}
}
]
},
{
"dob": "er",
"name": {
"first": " abc",
"last": "txt"
},
"start_date": {
"moth": "2",
"day": "5",
"year": 1
},
"children": [
{
"child": {
"id": "1",
"desc": "first child"
}
},
{
"child": {
"id": "2",
"desc": "second child"
}
}
]
}
]
let new_dataset = old_dataset.map((arr) => {
arr.name = `${arr.name.first} ${arr.name.last}`
arr.start_date = `${arr.start_date.moth} ${arr.start_date.day} ${arr.start_date.year}`
return arr
})
console.log(new_dataset)

Categories

Resources