I have an Json Raw Data with Parent child relationship.I would like to generate Hierarchical json from flat with parent ID.I have tried below code its working fine but I would like to know how to remove children[] object if there is no data in children object.
var arry = [
{ Id: "1", Name: "abc", Parent: "", attr: "abc" },
{ Id: "2", Name: "abc", Parent: "1", attr: "abc" },
{ Id: "3", Name: "abc", Parent: "2", attr: "abc" },
{ Id: "4", Name: "abc", Parent: "2", attr: "abc" },
];
function convert(array) {
var map = {};
for (var i = 0; i < array.length; i++) {
var obj = array[i];
obj.children = [];
map[obj.Id] = obj;
var parent = obj.Parent || "-";
if (!map[parent]) {
map[parent] = {
children: [],
};
}
map[parent].children.push(obj);
}
return map["-"].children;
}
var r = convert(arry);
console.log("array", r);
console.log("result", JSON.stringify(r));
I have the feeling that there might be a better way, but how about just traverse through the map properties and delete the ones with an empty children array?
var arry = [
{ Id: "1", Name: "abc", Parent: "", attr: "abc" },
{ Id: "2", Name: "abc", Parent: "1", attr: "abc" },
{ Id: "3", Name: "abc", Parent: "2", attr: "abc" },
{ Id: "4", Name: "abc", Parent: "2", attr: "abc" },
];
function convert(array) {
var map = {};
for (var i = 0; i < array.length; i++) {
var obj = array[i];
obj.children = [];
map[obj.Id] = obj;
var parent = obj.Parent || "-";
if (!map[parent]) {
map[parent] = {
children: [],
};
}
map[parent].children.push(obj);
}
for (let prop in map) {
if (map[prop].children.length === 0) {
delete map[prop].children;
}
}
return map["-"].children;
}
var r = convert(arry);
console.log("array", r);
console.log("result", JSON.stringify(r));
Using map-reduce can be done very easily in O(n)
const source = [
{ Id: "1", Name: "abc", Parent: "", attr: "abc" },
{ Id: "2", Name: "abc", Parent: "1", attr: "abc" },
{ Id: "3", Name: "abc", Parent: "2", attr: "abc" },
{ Id: "4", Name: "abc", Parent: "2", attr: "abc" },
];
function tree(data, id, pId) {
const [result] = data.reduce(
([r, map], item) => {
const d = { ...item, children: [] };
const loc = map[item[pId]];
if (loc) {
loc.children.push(d);
} else {
r.push(d);
}
map[item[id]] = d;
return [r, map];
},
[[], {}]
);
return result;
}
//console.log(JSON.stringify(tree(source, "Id", "Parent"), null, 4));
document.querySelector("pre").innerHTML = JSON.stringify(tree(source, "Id", "Parent"), null, 4)
.as-console {
min-height: 100% !important;
}
.as-console-row {
color: blue !important;
}
.code {
<pre class="code"></pre>
Related
unable to change the items based on id and expected result should be in output format
const items = [
{ id: 1, value: "first" },
{ id: 2, value: "second" },
{ id: 3, value: "third" }
];
const expectedOutput = [
{ id: 1, value: "first" },
{ id: 2, value: "newvalue" },
{ id: 3, value: "third" }
]
function getData(value, id) {
return items.map((_each)=> {
if(_each.id === id) {
//need to update items with id=2
}
})
}
console.log(getData("newvalue", 2))
let inputArr = {
"data": [{
"id": 1,
"value": "first",
"row": "A"
},
{
"id": 2,
"value": "second",
"row": "A"
},
{
"id": 3,
"value": "Third",
"row": "B"
},
{
"id": 4,
"value": "Fourth",
"row": "B"
}
]
}
function format(inputArr) {
let arr = []
let obj = {};
inputArr.data.forEach(el => {
obj = {
...obj,
[el.row]: [...(obj[el.row] || []) , el.value],
}
});
arr.push(obj);
return arr;
}
let outputArr = format(inputArr)
console.log(outputArr)
let expectedOutput = [{
"A": ["first", "second"]
}, {
"B": ["Third", "Fourth"]
}]
#chidananda,
Map callback should return updated item. Minor modification to your code would work!
const items = [
{ id: 1, value: "first" },
{ id: 2, value: "second" },
{ id: 3, value: "third" }
];
const expectedOutput = [
{ id: 1, value: "first" },
{ id: 2, value: "newvalue" },
{ id: 3, value: "third" }
]
function getData(value, id) {
return items.map((_each)=> {
if(_each.id === id) {
_each.value = value;
}
return _each; // Return the modified item
})
}
console.log(getData("newvalue", 2))
I have the following data stored in a variable:
let categories = [
{
name: "a",
nodes: [
{
name: "aa",
nodes: [
{
name: "aaa"
}
]
},
{
name: "ab",
},
{
name: "ac",
},
{
name: "ad",
}
]
},
{
name: "b",
nodes: [
{
name: "ba",
},
{
name: "bb",
},
{
name: "bc",
},
{
name: "bd",
}
]
}
];
And I have the following recursive function which accepts the categories variable and name.
function getCategoryParents(categories, name) {
for (let index = 0; index < categories.length; index++) {
const category = categories[index];
if (category.name === name) {
}
if (category.nodes && category.nodes.length) {
category.nodes.forEach(cat => this.getCategoryParents([cat], name));
}
}
}
I want to return an array of names containing the name in the parameter and the parents of that name.
For example if I called getCategoryParents(categories, "aaa") it should returns ["a", "aa", "aaa"]. because aa is the parent of aaa and a is the parent of aa.
I hope it's clear.
I tweaked your function so it would actually return some values when it finds the matches :
function getCategoryParents(arr, name) {
for (let child of arr) {
if (child.name === name) {
return name;
} else if (child.nodes.length > 0) {
var x = getCategoryParents(child.nodes, name);
if (x) return Array.isArray(x) ? [child.name, ...x] : [child.name, x];
}
}
}
let categories = [
{
name: "a",
nodes: [
{
name: "aa",
nodes: [
{
name: "aaa"
}
]
},
{
name: "ab"
},
{
name: "ac"
},
{
name: "ad"
}
]
},
{
name: "b",
nodes: [
{
name: "ba"
},
{
name: "bb"
},
{
name: "bc"
},
{
name: "bd"
}
]
}
];
const result = getCategoryParents(categories, "aaa");
console.log(result); // ["a", "aa", "aaa"]
In my angular 8 application, I have 2 arrays:
array1 = [{
"SubType": "2|3|4|5|6",
},
{
"SubType": "2",
},
{
"SubType": "3|4",
},
{
"SubType": "6",
},
{
"SubType": "3|6",
},
]
&
array2 = [{
"id": 2,
"type": "1",
},
{
"id": 3,
"type": "5",
},
{
"id": 4,
"type": "4",
},
{
"id": 5,
"type": "3",
},
{
"id": 6,
"type": "2",
}
]
I am trying to check each "SubType" in array1 and see if that element(id) is present in array2 and if present assign its "type" to a variable. "SubType" is | separated which I and converting to an array using array1..split('|'). This when assigning to a variable will need to be comma separated. I tried using array filter but I am not able to find a way to loop thorough the second array. Can anyone help?
array1.forEach(reg => {
if (reg.SubType) {
let subTypeTemp = reg.SubType.split('|');
let tempVariable = subTypeTemp.some(ele => {
let stringToassign = '';
for (let i = 0; i < array2.length; i++) {
if (ele == array2[i].id) {
stringToassign += array2[i].type + ",";
}
}
})
}
})
const array1 = [
{
SubType: "2|3|4|5|6"
},
{ SubType: "2" },
{ SubType: "3|4" },
{ SubType: "6" },
{ SubType: "3|6" }
];
const array2 = [
{
id: 2,
type: "1"
},
{ id: 3, type: "5" },
{ id: 4, type: "4" },
{ id: 5, type: "3" },
{ id: 6, type: "2" }
];
const array2Obj = array2.reduce(
(acc, curr) => ({
...acc,
[curr.id]: curr.type
}),
{}
);
const types = [];
array1.forEach(item => {
const sub_types = item.SubType.split("|");
sub_types.forEach(st => {
if (st in array2Obj) {
types.push(array2Obj[st]);
}
});
});
const types_str = [...new Set(types)].join(',');
console.log("types", types_str);
You could take a Map and prevent looping array2 over and over for getting type of a wanted id.
var array1 = [{ SubType: "2|3|4|5|6" }, { SubType: "2" }, { SubType: "3|4" }, { SubType: "6" }, { SubType: "3|6" }],
array2 = [{ id: 2, type: "1" }, { id: 3, type: "5" }, { id: 4, type: "4" }, { id: 5, type: "3" }, { id: 6, type: "2" }],
types = new Map(array2.map(({ id, type }) => [id.toString(), type])),
result = array1.map(({ SubType }) => SubType
.split('|')
.map(Map.prototype.get, types)
.join()
);
console.log(result);
Is there a way good way JS/ES6 to loop through an object and it's children and creating new object tree array.
I have this json tree object:
[
{
id: "001",
deparmentsIds: [
"002",
"003"
],
details: {
parentDeparmentsId: null,
name: "Top"
}
},
{
id: "002",
deparmentsIds:[
"004"
],
details: {
parentDeparmentsId: ["001"],
name: "Operations"
}
},
{
id: "003",
deparmentsIds:[]
details: {
parentDeparmentsId: ["001"],
name: "Support"
}
},
{
id: "004",
deparmentsIds:[]
details: {
parentDeparmentsId: ["002"],
name: "Support operations"
}
}
]
I want to create new object array tree that looks like this:
You could create recursive function with reduce and map method to create nested object structure.
const data = [{"id":"001","deparmentsIds":["002","003"],"details":{"parentDeparmentsId":null,"name":"Top"}},{"id":"002","deparmentsIds":["004"],"details":{"parentDeparmentsId":"001","name":"Operations"}},{"id":"003","deparmentsIds":[],"details":{"parentDeparmentsId":"001","name":"Support"}},{"id":"004","deparmentsIds":[],"details":{"parentDeparmentsId":"002","name":"Support operations"}}]
function tree(input, parentId) {
return input.reduce((r, e) => {
if (e.id == parentId || parentId == undefined && e.details.parentDeparmentsId == null) {
const children = [].concat(...e.deparmentsIds.map(id => tree(input, id)))
const obj = {
[e.details.name]: children
}
r.push(obj)
}
return r;
}, [])
}
const result = tree(data)
console.log(result)
You could collect all information in an object with a single loop and return only the nodes with no parent.
function getTree(data, root) {
var o = {};
data.forEach(({ id, details: { parentDeparmentsId: parent, name } }) => {
var temp = { id, name };
if (o[id] && o[id].children) {
temp.children = o[id].children;
}
o[id] = temp;
o[parent] = o[parent] || {};
o[parent].children = o[parent].children || [];
o[parent].children.push(temp);
});
return o[root].children;
}
var data = [{ id: "001", deparmentsIds: ["002", "003"], details: { parentDeparmentsId: null, name: "Top" } }, { id: "002", deparmentsIds: ["004"], details: { parentDeparmentsId: ["001"], name: "Operations" } }, { id: "003", deparmentsIds: [], details: { parentDeparmentsId: ["001"], name: "Support" } }, { id: "004", deparmentsIds: [], details: { parentDeparmentsId: ["002"], name: "Support operations" } }],
tree = getTree(data, null);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
http://jsfiddle.net/0444046b/12/
I have a complex Array of objects, each object has it's own tag Array.
I also have just an object which should match one of the objects in the tag Array, and if so remove that tag.
Got some help here, however my example there was too simple, so far no luck with this below.
Basically I have the object tag and I need to remove it from the tags Array inside of tagsArray.
var tagsArray = [{
name: "group1",
tags: [
{
name: "1",
tag_id: "1234"
},
{
name: "2",
tag_id: "5678"
},
{
name: "3",
tag_id: "9012"
}
]
},
{
name: "group2",
tags: []
}
];
console.log(tagsArray[0]);
// Need to find this inside of tags inside of tagsArray and remove it:
var tag = {
name: "3",
tag_id: "9012"
}
var temp_array = [];
temp_array.push(tag);
var map = {};
tagsArray.forEach(function(obj, index) {
map[obj.tag_id] = index;
});
console.log(map);
temp_array.forEach(function(obj) {
if ( obj.tag_id ) {
tagsArray.splice(tagsArray[map[obj.tag_id]]);
console.log(tagsArray);
}
});
You can loop through each item in tagsArray and then filter our matching elements in the inner tags property.
var tagsArray = [{
name: "group1",
tags: [{
name: "1",
tag_id: "1234"
}, {
name: "2",
tag_id: "5678"
}, {
name: "3",
tag_id: "9012"
}]
}, {
name: "group2",
tags: []
}];
// Need to find this inside of tags inside of tagsArray and remove it:
var removeTag = {
name: "3",
tag_id: "9012"
}
var message = 'Before:<br>' + JSON.stringify(tagsArray) + '<br><br>';
tagsArray.forEach(function(element) {
element.tags = element.tags.filter(function(tag) {
return tag.name != removeTag.name && tag.tag_id != removeTag.tag_id;
})
});
message += 'After:<br>' + JSON.stringify(tagsArray);
document.body.innerHTML = message
The solution of Daniel Imms is totally fine, but this one also can solve your problem, and it is a bit faster.
var tagsArray = [{
name: "group1",
tags: [{
name: "1",
tag_id: "1234"
}, {
name: "2",
tag_id: "5678"
}, {
name: "3",
tag_id: "9012"
}]
}, {
name: "group2",
tags: [{
name: "4",
tag_id: "1012"
}]
}];
var removedTag = {
name: "4",
tag_id: "1012"
};
var message = 'Before:</br>' + JSON.stringify(tagsArray) + '</br></br>';
tagsArray.forEach(function(obj, i) {
obj.tags.forEach(function(tag, j) {
if (tag.tag_id === removedTag.tag_id && tag.name === removedTag.name) {
obj.tags.splice(j, 1);
return;
}
});
});
message += 'After:</br>' + JSON.stringify(tagsArray);
document.body.innerHTML = message
I tested with jsPerf and here is the link and here is the result.