I have an array of objects that can have other arrays of object as children (it can have a complex hierarchy): if an object has a children prop — it's a group, if it doesn't — it's an item:
var myData = [
{
"name": "item 1"
},
{
"name": "group 1",
"children": [
{
"name": "item in a group 1"
}]
},
{
"name": "group 2",
"children": [
{
"name": "group 1 in a group 2",
"children": [
{
"name": "item in a nested group 1"
},
{
"name": "deeply nested group",
"children": [
{
"name": "deep item"
}]
},]
},
{
"name": "group 2 in a group 2",
"children": [
{
"name": "item in a nested group 2"
}]
}]
}]
and I want to parse it to have a flat object that would have all groups as props and items as arrays, each prop would also include items from grandchildren groups.
Currently with the code below I'm able to flatten the array and have groups as props and items as arrays for each object:
{
"root": [
"item 1"
],
"group 1": [
"item in a group 1"
],
"group 2": [],
"group 1 in a group 2": [
"item in a nested group 1"
],
"deeply nested group": [
"deep item"
],
"group 2 in a group 2": [
"item in a nested group 2"
]
}
However I 'm struggling with adding grandchildren. Here's the desired output:
{
"root": [
"item 1"
],
"group 1": [
"item in a group 1"
],
"group 2": [
"item in a nested group 1", // includes items of child groups
"item in a nested group 2",
"deep item"
],
"group 1 in a group 2": [
"item in a nested group 1",
"deep item" // includes item of a child group
],
"deeply nested group": [
"deep item"
],
"group 2 in a group 2": [
"item in a nested group 2"
]
}
here's my code I'm using (I can only use older version of pure JS so no ES6, NPM; can use polyfill though)
var myItems = {
'root': []
}
var i;
for (i = 0; i < myData.length; i++)
{
parseItems(myData[i], 'root')
}
function parseItems(data, groupName)
{
var key, i;
for (key in data)
{
if (!data.hasOwnProperty(key))
continue;
else if (data.hasOwnProperty('children'))
{
groupName = data['name']
if (myItems[groupName] == undefined) myItems[groupName] = []
for (i = 0; i < data[key].length; i++)
{
parseItems(data[key][i], groupName);
}
}
else
{
myItems[groupName].push(data['name']);
}
}
}
And I don't understand how can I make a version of this code (or something better probably?) that'd fill parent groups with grandchildren items.
Ancient version.
function flat(array, parents, target) {
var i, j;
parents = parents || [];
target = target || { root: [] };
for (i = 0; i < array.length; i++) {
if (array[i].children) {
flat(array[i].children, parents.concat(array[i].name), target);
continue;
}
if (parents.length) {
for (j = 0; j < parents.length; j++) {
if (!target[parents[j]]) target[parents[j]] = [];
target[parents[j]].push(array[i].name);
}
} else {
target.root.push(array[i].name);
}
}
return target;
}
var data = [{ name: "item 1" }, { name: "group 1", children: [{ name: "item in a group 1" }] }, { name: "group 2", children: [{ name: "group 1 in a group 2", children: [{ name: "item in a nested group 1" }, { name: "deeply nested group", children: [{ name: "deep item" }] }] }, { name: "group 2 in a group 2", children: [{ name: "item in a nested group 2" }] }] }],
result = flat(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
I have a file of JSON-serialized data that looks like this:
{
"name":"Store",
"children":[
{
"name":"Store 1",
"children":[
{
"name":"collection 1",
"description":"collection 1",
"children":[
{
"name":"Products",
"description":"Products",
"children":[
{
"name":"Product 1",
"description":"Product 1"
}
]
}
]
}
],
"description":"category 1"
},
{
"name":"Store 2"
}
]
}
For objects with a name property, I want to add a title whose value is the same as the value of the name property. Below is what I am trying to convert my JSON to:
{
"name":"Store",
"title":"Store",
"children":[
{
"name":"Store 1",
"title":"Store 1",
"children":[
{
"name":"collection 1",
"title":"collection 1",
"description":"collection 1",
"children":[
{
"name":"Products",
"title":"Products",
"description":"Products",
"children":[
{
"name":"Product 1",
"title":"Product 1",
"description":"Product 1"
}
]
}
]
}
],
"description":"category 1"
},
{
"name":"Store 2",
"title":"Store 2"
}
]
}
we can use parse the Json using JSON.Parse and use recursion to add title as below to all the children
function Recursion(items) {
items["title"] = items["name"]
if (items["children"] != undefined) {
items["children"].forEach(element => {
element = Recursion(element)
});
}
return items
}
var text = '{"name":"Store","children":[{"name":"Store 1","children":[{"name":"collection 1","description":"collection 1","children":[{"name":"Products","description":"Products","children":[{"name":"Product 1","description":"Product 1"}]}]}],"description":"category 1"},{"name":"Store 2"}]}';
var item = JSON.parse(text);
item = Recursion(item);
const addTitleRec = (j) => {
j.title = j.name, j.children && j.children.forEach(addTitleRec);
}
const json = {
"name": "Store",
"children": [{
"name": "Store 1",
"children": [{
"name": "collection 1",
"description": "collection 1",
"children": [{
"name": "Products",
"description": "Products",
"children": [{
"name": "Product 1",
"description": "Product 1"
}]
}]
}],
"description": "category 1"
},
{
"name": "Store 2"
}
]
};
addTitleRec(json);
console.log(json);
I have a nested array of objects and want to get only the child property elements of an array. This is just an example and the actual data will include a unique children property in separate indices in the array. I am only able to traverse the first array in the list.
Here is my implementation:
const headers = [{
id: "name1",
title: "Name 1",
children: [{
title: "Children 1",
child: [{
title: "Child 1",
onClick: "child1Click"
},
{
title: "Child 2",
onClick: "child2Click"
}
]
},
{
title: "CHildren 2",
child: [{
title: "Child 3",
id: "child3Click"
},
{
title: "Child 4",
id: "child4Click"
}
]
}
]
},
{
id: "name2",
title: "Name 2",
children: [{
title: "Children 3",
child: [{
title: "Child 5",
onClick: "child5Click"
},
{
title: "Child 6",
onClick: "child6Click"
}
]
},
{
title: "CHildren 4",
child: [{
title: "Child 7",
id: "child7Click"
},
{
title: "Child 8",
id: "child8Click"
}
]
}
]
},
{
id: "name3",
title: "Name 3"
},
{
id: "name4",
title: "Name 4"
}
]
console.log(_.flattenDeep(_.map(_.compact(_.map(headers, item => item.children))[0], item1 => item1.child)))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
Expected Output:
[
{
"title": "Child 1",
"onClick": "child1Click"
},
{
"title": "Child 2",
"onClick": "child2Click"
},
{
"title": "Child 3",
"id": "child3Click"
},
{
"title": "Child 4",
"id": "child4Click"
},
{
"title": "Child 5",
"onClick": "child5Click"
},
{
"title": "Child 6",
"onClick": "child6Click"
},
{
"title": "Child 7",
"id": "child7Click"
},
{
"title": "Child 8",
"id": "child8Click"
}
]
Please advice.
Edit: I was able to get the required result using console.log(.flattenDeep(.map(.flattenDeep(.compact(_.map(headers, 'children'))), 'child')))
But is there an optimized version for doing the same? Thanks
Get the children with _.flatMap(), filter out the undefined values, and then use _.flatMap() again to get the values of the child property:
const headers = [{"id":"name1","title":"Name 1","children":[{"title":"Children 1","child":[{"title":"Child 1","onClick":"child1Click"},{"title":"Child 2","onClick":"child2Click"}]},{"title":"CHildren 2","child":[{"title":"Child 3","id":"child3Click"},{"title":"Child 4","id":"child4Click"}]}]},{"id":"name2","title":"Name 2","children":[{"title":"Children 3","child":[{"title":"Child 5","onClick":"child5Click"},{"title":"Child 6","onClick":"child6Click"}]},{"title":"CHildren 4","child":[{"title":"Child 7","id":"child7Click"},{"title":"Child 8","id":"child8Click"}]}]},{"id":"name3","title":"Name 3"},{"id":"name4","title":"Name 4"}]
const result = _.flatMap(_.compact(_.flatMap(headers, 'children')), 'child')
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
If you're using lodash/fp, you can generate a more readable function with _.flow():
const fn = _.flow(
_.flatMap('children'),
_.compact,
_.flatMap('child')
)
const headers = [{"id":"name1","title":"Name 1","children":[{"title":"Children 1","child":[{"title":"Child 1","onClick":"child1Click"},{"title":"Child 2","onClick":"child2Click"}]},{"title":"CHildren 2","child":[{"title":"Child 3","id":"child3Click"},{"title":"Child 4","id":"child4Click"}]}]},{"id":"name2","title":"Name 2","children":[{"title":"Children 3","child":[{"title":"Child 5","onClick":"child5Click"},{"title":"Child 6","onClick":"child6Click"}]},{"title":"CHildren 4","child":[{"title":"Child 7","id":"child7Click"},{"title":"Child 8","id":"child8Click"}]}]},{"id":"name3","title":"Name 3"},{"id":"name4","title":"Name 4"}]
const result = fn(headers)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>
Using lodash with _.flow() and _.partialRight():
const pr = _.partialRight;
const fn = _.flow(
pr(_.flatMap, 'children'),
_.compact,
pr(_.flatMap, 'child')
)
const headers = [{"id":"name1","title":"Name 1","children":[{"title":"Children 1","child":[{"title":"Child 1","onClick":"child1Click"},{"title":"Child 2","onClick":"child2Click"}]},{"title":"CHildren 2","child":[{"title":"Child 3","id":"child3Click"},{"title":"Child 4","id":"child4Click"}]}]},{"id":"name2","title":"Name 2","children":[{"title":"Children 3","child":[{"title":"Child 5","onClick":"child5Click"},{"title":"Child 6","onClick":"child6Click"}]},{"title":"CHildren 4","child":[{"title":"Child 7","id":"child7Click"},{"title":"Child 8","id":"child8Click"}]}]},{"id":"name3","title":"Name 3"},{"id":"name4","title":"Name 4"}]
const result = fn(headers)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
I worked nested object which I want to convert into recursive array or nested array.
I tried iterate the object something like below but it was creating single object of array.
Can anyone give suggestions or share your ideas it will helpful for me
iterate(obj) {
for (let property in obj) {
this.configArray.push({key: property,children: [], isValue: false, value: ''});
if (obj.hasOwnProperty(property)) {
const index = Object.keys(obj).indexOf(property);
if (typeof obj[property] == "object") {
this.iterate(obj[property]);
}
else {
this.configArray[index].children.push({ key: property, value: obj[property], isValue: true, children: [] });
}
}
}
}
INPUT
{
"Parent 1": {
"11": "Child 1",
"12": "Child 2",
},
"Parent 2": {
"20": {
"21": "Grand Child 1",
"22": "Grand Child 2",
}
},
"Parent 3": {
"31": "Child 1",
"32": "Child 2",
}
}
OUTPUT
[
{
key: "Parent 1",
value: "",
children: [
{ key: 11, value: "Child 1", children: [] },
{ key: 12, value: "Child 2", children: [] }
]
},
{
key: "Parent 2",
value: "",
children: [
{
key: 20,
value: "",
children: [
{ key: 21, value: "Grand Child 1", children: [] },
{ key: 22, value: "Grand Child 2", children: [] }
]
}
]
},
{
key: "Parent 3",
value: "",
children: [
{ key: 31, value: "Child 1", children: [] },
{ key: 32, value: "Child 2", children: [] }
]
},
];
You could take a recursive approach and map the value or the children, if value is an object.
function transform(object) {
return Object
.entries(object)
.map(([key, value]) => Object.assign({ key }, value && typeof value === 'object'
? { value: '', children: transform(value) }
: { value, children: [] }
));
}
var data = { "Parent 1": { 11: "Child 1", 12: "Child 2" }, "Parent 2": { 20: { 21: "Grand Child 1", 22: "Grand Child 2" } }, "Parent 3": { 31: "Child 1", 32: "Child 2" } },
result = transform(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
{
"data": [{
"prop": "prop1",
"template": "template1",
"group": [{
"group_name": "Group 1",
}, {
"group_name": "Group 2",
}, {
"group_name": "Group 3",
}]
}]
}
In the above json I need push the group_no based on group_name. i.e. If group_name is equal to Group 1 then group_no is equal to 10. and form the following json. How do I update the existing json?
{
"data": [{
"prop": "prop1",
"template": "template1",
"group": [{
"group_name": "Group 1",
"group_no": "10"
}, {
"group_name": "Group 2",
"group_no": "11"
}, {
"group_name": "Group 3",
"group_no": "12"
}]
}]
}
Do it this way.
var json = {
"data": [{
"prop": "prop1",
"template": "template1",
"group": [{
"group_name": "Group 1",
}, {
"group_name": "Group 2",
}, {
"group_name": "Group 3",
}]
}]
};
json.data[0].group.forEach(function(d){
if(d.group_name=="Group 1")
d.group_no = 10;
else if(d.group_name=="Group 2")
d.group_no = 11;
else
d.group_no = 12;
});
alert(JSON.stringify(json));
Not sure what you mean by not looping, for you need to loop through it anyway. But what you want is done like this:
data[0].group = data[0].group.map(function(el) {
switch (el.group_name) {
case 'Group 1': return el.group_no = '10';
case 'Group 2': return el.group_no = '11';
...
default: return el;
}
});
If your JSON object is actually text, you first to var parsed = JSON.parse(data); and work with parsed instead of data. Afterwords you can data = JSON.stringify(parsed);
I have 2 JSON arrays, both of which can contain nested arrays:
var serverArr = [
{ "id": 1, "text": "Item 1" },
{ "id": 2, "text": "Item 2" },
{ "id": 3, "text": "Item 3", "children": [{ "id": 20, "text": "Item 20" },
{ "id": 21, "text": "Item 21" }] },
{ "id": 4, "text": "Item 4" }
];
var userArr = [
{ "id": 1, "text": "Item 1" },
{ "id": 3, "text": "Item 3", "children": [{ "id": 20, "text": "Item 20" },
{ "id": 25, "text": "Item 25" }] },
{ "id": 5, "text": "Item 5" }
];
What I need to do is combine them into 1 array, only taking the matching values. So the result should look like:
[{ "id": 1, "text": "Item 1" },
{ "id": 3, "text": "Item 3", "children": [{ "id": 20, "text": "Item 20" }]}];
I'm using the results with the jsTree plugin, so the format has to be like this unfortunately.
How can I get the end result from those 2 arrays?
I have defined a function that does 3 things to solve this problem
First thing it gets all the elements that are in both arrays and it collects all childs for every item in both arrays
Second thing is filtering all the childs elements to keep only unique ones
Third thing is changing the children of the parent elements to keep only unique ones
function combine(array1,array2)
{
result = serverArr.concat(userArr);
var childs = [];
// First step
result = result.filter(function(elem, index, self)
{
if(elem.children != undefined)
childs[elem.id] = ((childs[elem.id] == undefined) ? [] : childs[elem.id]).concat(elem.children);
for (key in self)
if(key < index && elem.id == self[key].id )
return true;
return false;
});
// Second step
for(i in childs)
childs[i] = childs[i].filter(function(elem, index, self)
{
for (key in self)
if(key < index && JSON.stringify(elem) == JSON.stringify(self[key]) )
return true;
return false;
});
//Third step
for(key in result)
if(childs[result[key].id] != undefined)
result[key].children = childs[result[key].id];
return result;
}