Find empty arrays in nested array and remove them in Javascript - javascript

I have a nested and hierarchical array of objects, which looks like this
[{
"id": 0,
"name": "E00-E90 Stoffwechselstörungen",
"parentId": null,
"children": [{
"id": 1,
"name": "E70-E90 Stoffwechselstörungen",
"parentId": 0,
"children": [{
"id": 2,
"name": "E70.- Störungen des Stoffwechsels aromatischer Aminosäuren",
"parentId": 1,
"children": []
}, {
"id": 3,
"name": "E71.- Störungen des Stoffwechsels verzweigter Aminosäuren und des Fettsäurestoffwechsels",
"parentId": 1,
"children": []
}, {
"id": 4,
"name": "E72.- Sonstige Störungen des Aminosäurestoffwechsels",
"parentId": 1,
"children": []
},
...
Now I want to remove the empty array "children": [] from the last children.
I've tried it with reduce but it doesn't work without any error.
var lastElementLength = list.length - 1
const findItemNested = (arr, itemId, nestingKey) => (
arr.reduce((a, item) => {
if (a) return a;
if (item.id === itemId) return item;
if (item[nestingKey]) return findItemNested(item[nestingKey], itemId, nestingKey)
}, null)
);
const resEmptyChildren = findItemNested(roots, lastElementLength, "children");
console.log('resEmptyChildren', resEmptyChildren)

You could use recursion for that.
var tInput = [{
"id": 0,
"name": "E00-E90 Stoffwechselstörungen",
"parentId": null,
"children": [{
"id": 1,
"name": "E70-E90 Stoffwechselstörungen",
"parentId": 0,
"children": [{
"id": 2,
"name": "E70.- Störungen des Stoffwechsels aromatischer Aminosäuren",
"parentId": 1,
"children": []
}, {
"id": 3,
"name": "E71.- Störungen des Stoffwechsels verzweigter Aminosäuren und des Fettsäurestoffwechsels",
"parentId": 1,
"children": []
}, {
"id": 4,
"name": "E72.- Sonstige Störungen des Aminosäurestoffwechsels",
"parentId": 1,
"children": []
}]
}]
}];
(function removeEmptyChildrenProperties(input){
console.log('Checking id:', input.id);
if(input.hasOwnProperty('children')){
if(input.children && input.children.length){
input.children.forEach(removeEmptyChildrenProperties)
}
else{
console.log('Remove children on id:', input.id);
delete input.children
}
};
return input
}).apply(null, tInput);
console.log(JSON.stringify(tInput));

Related

Return children tree from array

I have an array of regions the highest region has key: 10 and parent_id: null and I want to restructure this array to return a tree.
Regions tree should look like if the input is [10]
Egypt
Zone 1
Tagamo3
Giza
Helwan
Fayoum
Zone 2
Gesr ElSuis
test
Delta
Mohandeseen
Down Town
Array:
[
{
"key": 1,
"title": "Zone 1",
"parent_id": 10
},
{
"key": 2,
"title": "Zone 2",
"parent_id": 10
},
{
"key": 3,
"title": "Tagamo3",
"parent_id": 1
},
{
"key": 4,
"title": "Gesr ElSuis",
"parent_id": 2
},
{
"key": 5,
"title": "Delta",
"parent_id": 2
},
{
"key": 6,
"title": "Mohandeseen",
"parent_id": 2
},
{
"key": 7,
"title": "Giza",
"parent_id": 1
},
{
"key": 8,
"title": "Helwan",
"parent_id": 1
},
{
"key": 9,
"title": "Down Town",
"parent_id": 2
},
{
"key": 10,
"title": "Egypt",
"parent_id": null
},
{
"key": 11,
"title": "Fayoum",
"parent_id": 1
},
{
"key": 12,
"title": "test",
"parent_id": 4
}
]
The output I want to achieve if input is [10]:
[
{
"key": 10,
"title": "Egypt",
"parent_id": null,
"children": [
{
"key": 1,
"title": "Zone 1",
"parent_id": 10,
"children": [
{
"key": 3,
"title": "Tagamo3",
"parent_id": 1,
"children": []
},
{
"key": 7,
"title": "Giza",
"parent_id": 1,
"children": []
},
{
"key": 8,
"title": "Helwan",
"parent_id": 1,
"children": []
},
{
"key": 11,
"title": "Fayoum",
"parent_id": 1,
"children": []
}
]
},
{
"key": 2,
"title": "Zone 2",
"parent_id": 10,
"children": [
{
"key": 4,
"title": "Gesr ElSuis",
"parent_id": 2,
"children": [
{
"key": 12,
"title": "test",
"parent_id": 4,
"children": []
}
]
},
{
"key": 5,
"title": "Delta",
"parent_id": 2,
"children": []
},
{
"key": 6,
"title": "Mohandeseen",
"parent_id": 2,
"children": []
},
{
"key": 9,
"title": "Down Town",
"parent_id": 2,
"children": []
}
]
}
]
}
]
Regions tree should look like if the input is [1,2]
Zone 1
Tagamo3
Giza
Helwan
Fayoum
Zone 2
Gesr ElSuis
test
Delta
Mohandeseen
Down Town
Regions tree should look like if the input is [1]
Zone 1
Tagamo3
Giza
Helwan
Fayoum
May not be the most optimized, but I gave it a try:
const arr = [{key:1,title:"Zone 1",parent_id:10},{key:2,title:"Zone 2",parent_id:10},{key:3,title:"Tagamo3",parent_id:1},{key:4,title:"Gesr ElSuis",parent_id:2},{key:5,title:"Delta",parent_id:2},{key:6,title:"Mohandeseen",parent_id:2},{key:7,title:"Giza",parent_id:1},{key:8,title:"Helwan",parent_id:1},{key:9,title:"Down Town",parent_id:2},{key:10,title:"Egypt",parent_id:null},{key:11,title:"Fayoum",parent_id:1},{key:12,title:"test",parent_id:4}];
const buildTree = key => arr.filter(x => x.parent_id === key)
.map(x => ({ ...x, children: buildTree(x.key) }));
console.log(buildTree(null));
To build multiple trees, this could work:
const arr = [{key:1,title:"Zone 1",parent_id:10},{key:2,title:"Zone 2",parent_id:10},{key:3,title:"Tagamo3",parent_id:1},{key:4,title:"Gesr ElSuis",parent_id:2},{key:5,title:"Delta",parent_id:2},{key:6,title:"Mohandeseen",parent_id:2},{key:7,title:"Giza",parent_id:1},{key:8,title:"Helwan",parent_id:1},{key:9,title:"Down Town",parent_id:2},{key:10,title:"Egypt",parent_id:null},{key:11,title:"Fayoum",parent_id:1},{key:12,title:"test",parent_id:4}];
const buildNode = x => ({...x, children: buildTree(x.key)});
const buildTree = key => arr.filter(x => x.parent_id === key)
.map(buildNode);
const buildTrees = keys => arr.filter(x => keys.includes(x.key))
.map(buildNode);
console.log(buildTrees([1, 2]));
based on jcalz snippet, but requires only a single pass over the input array.
const arr = [{key:1,title:"Zone 1",parent_id:10},{key:2,title:"Zone 2",parent_id:10},{key:3,title:"Tagamo3",parent_id:1},{key:4,title:"Gesr ElSuis",parent_id:2},{key:5,title:"Delta",parent_id:2},{key:6,title:"Mohandeseen",parent_id:2},{key:7,title:"Giza",parent_id:1},{key:8,title:"Helwan",parent_id:1},{key:9,title:"Down Town",parent_id:2},{key:10,title:"Egypt",parent_id:null},{key:11,title:"Fayoum",parent_id:1},{key:12,title:"test",parent_id:4}];
/*const lookup: Record<number|"roots", Tree[]> = { roots: [] };*/
const lookup = { roots: [] };
for (const item of arr) {
// get or create the "children" array for my parent.
// parent may not be known yet, but I'm already collecting its children, my siblings.
const siblings = lookup[item.parent_id ?? "roots"] ??= [];
// add myself to that children array.
siblings.push({
...item,
// get or create my children array.
children: lookup[item.key] ??= []
});
}
// it's up to you to handle `lookup.roots.length !== 1`
console.log(lookup.roots);
.as-console-wrapper{top:0;max-height:100%!important}

Filter nested objects by property value recursively and keep array structure

I have the following array of objects with nested elements in the children property. I need to get objects by their id if the id matches.
[
{
"id": 10,
"name": "Scenarios",
"value": null,
"children": [
{
"id": 12,
"name": "Scenario status",
"value": null,
"children": []
}
]
},
{
"id": 11,
"name": "Forecast source",
"value": null,
"children": []
},
{
"id": 16787217,
"name": "Item#Cust",
"value": null,
"children": [
{
"id": 16787230,
"name": "Customer",
"value": null,
"children": [
{
"id": 16787265,
"name": "Site",
"value": null,
"children": []
},
{
"id": 16787291,
"name": "Commercial Network",
"value": null,
"children": []
},
{
"id": 16787296,
"name": "Distribution Site",
"value": null,
"children": []
}
]
},
{
"id": 16787245,
"name": "Item#Site",
"value": null,
"children": [
{
"id": 16787266,
"name": "Family#Warehouse",
"value": null,
"children": []
}
]
},
{
"id": 16787254,
"name": "Item",
"value": null,
"children": [
{
"id": 16787260,
"name": "Family",
"value": null,
"children": [
{
"id": 16787264,
"name": "Product line",
"value": null,
"children": []
}
]
},
{
"id": 16787261,
"name": "Group 1",
"value": null,
"children": []
}
]
}
]
},
{
"id": 16787267,
"name": "Supplier",
"value": null,
"children": []
},
{
"id": 16787297,
"name": "SKU",
"value": null,
"children": []
}
]
If no match on root element is found, the function should lookup its children to see if there is a match, and the first children match should be pushed on the root level.
For exemple, I have a list of ids: [12, 16787217, 16787245, 16787266]
The function should return only objects where ids matches while keeping hierarchy if matching parent id have matching children id, so it should return this:
[
{
"id": 12,
"name": "Scenario status",
"value": null,
"children": []
},
{
"id": 16787217,
"name": "Item#Cust",
"value": null,
"children": [
{
"id": 16787245,
"name": "Item#Site",
"value": null,
"children": [
{
"id": 16787266,
"name": "Family#Warehouse",
"value": null,
"children": []
}
]
}
]
}
]
As for now, I can get only the first level elements if they are found with this function:
filterArray(array: Array<any>, ids: Array<number>) {
array = array.filter(el => ids.includes(el.id));
for (let i = 0; i < array.length; i++) {
this.filterArray(array[i].children, ids);
}
console.log(array);
}
Anyone have an idea on how to achieve this?
You could reduce the array and take the nodes with found id and children or only the children at the base level.
const
find = (r, { children = [], ...o }) => {
children = children.reduce(find, []);
if (ids.includes(o.id)) r.push({ ...o, children });
else if (children.length) r.push(...children);
return r;
},
data = [{ id: 10, name: "Scenarios", value: null, children: [{ id: 12, name: "Scenario status", value: null, children: [] }] }, { id: 11, name: "Forecast source", value: null, children: [] }, { id: 16787217, name: "Item#Cust", value: null, children: [{ id: 16787230, name: "Customer", value: null, children: [{ id: 16787265, name: "Site", value: null, children: [] }, { id: 16787291, name: "Commercial Network", value: null, children: [] }, { id: 16787296, name: "Distribution Site", value: null, children: [] }] }, { id: 16787245, name: "Item#Site", value: null, children: [{ id: 16787266, name: "Family#Warehouse", value: null, children: [] }] }, { id: 16787254, name: "Item", value: null, children: [{ id: 16787260, name: "Family", value: null, children: [{ id: 16787264, name: "Product line", value: null, children: [] }] }, { id: 16787261, name: "Group 1", value: null, children: [] }] }] }, { id: 16787267, name: "Supplier", value: null, children: [] }, { id: 16787297, name: "SKU", value: null, children: [] }],
ids = [12, 16787217, 16787245, 16787266],
result = data.reduce(find, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could also try this
let data = [
{
"id": 10,
"name": "Scenarios",
"value": null,
"children": [
{
"id": 12,
"name": "Scenario status",
"value": null,
"children": []
}
]
},
{
"id": 11,
"name": "Forecast source",
"value": null,
"children": []
},
{
"id": 16787217,
"name": "Item#Cust",
"value": null,
"children": [
{
"id": 16787230,
"name": "Customer",
"value": null,
"children": [
{
"id": 16787265,
"name": "Site",
"value": null,
"children": []
},
{
"id": 16787291,
"name": "Commercial Network",
"value": null,
"children": []
},
{
"id": 16787296,
"name": "Distribution Site",
"value": null,
"children": []
}
]
},
{
"id": 16787245,
"name": "Item#Site",
"value": null,
"children": [
{
"id": 16787266,
"name": "Family#Warehouse",
"value": null,
"children": []
}
]
},
{
"id": 16787254,
"name": "Item",
"value": null,
"children": [
{
"id": 16787260,
"name": "Family",
"value": null,
"children": [
{
"id": 16787264,
"name": "Product line",
"value": null,
"children": []
}
]
},
{
"id": 16787261,
"name": "Group 1",
"value": null,
"children": []
}
]
}
]
},
{
"id": 16787267,
"name": "Supplier",
"value": null,
"children": []
},
{
"id": 16787297,
"name": "SKU",
"value": null,
"children": []
}
]
let ids = [12, 16787217, 16787245, 16787266]
let filterArray = (mArray, ids) => {
let result = []
for (const x of mArray) {
if (ids.includes(x.id)) {
let element = {...x, children: []}
let childrenFounds = []
if (x.children.length) {
childrenFounds = filterArray(x.children, ids)
if (childrenFounds.length) element.children.push(...childrenFounds)
}else {
delete element.children
}
result.push(element)
} else if (x.children.length) {
let found = filterArray(x.children, ids)
if (found.length) result.push(...found)
}
}
return result
}
let res = filterArray(data, ids)
console.log('res', res)

Need to modify BE response to expected response at FE

BE Response:- which I am getting from Backend Service
{
"data": {
"type": "AnyType",
"resources": [
{
"id": 1,
"treeId": "1",
"name": "name1",
"description": "description1",
"children": [
{
"id": 3,
"treeId": "1-3",
"name": "subName1",
"description": "subDescription1",
"children": [
{
"id": 6,
"treeId": "1-3-6",
"name": "subSubName1",
"description": "subSubDesc1",
"children": []
}
]
}
]
},
{
"id": 2,
"treeId": "2",
"name": "name2",
"description": "description2",
"children": [
{
"id": 7,
"treeId": "2-7",
"name": "subName2",
"description": "subDescription2",
"children": []
}
]
}
]
}
}
But I need to modify this response to as below on FE
Expected Response:- means I need to join name and description field text to one(in name field ) as below:-
{
"data": {
"type": "AnyType",
"resources": [
{
"id": 1,
"treeId": "1",
"name": "name1-description1",
"description": "description1",
"children": [
{
"id": 3,
"treeId": "1-3",
"name": "subName1-subDescription1",
"description": "subDescription1",
"children": [
{
"id": 6,
"treeId": "1-3-6",
"name": "subSubName1-subSubDesc1",
"description": "subSubDesc1",
"children": []
}
]
}
]
},
{
"id": 2,
"treeId": "2",
"name": "name2-description2",
"description": "description2",
"children": [
{
"id": 7,
"treeId": "2-7",
"name": "subName2-subDescription2",
"description": "subDescription2",
"children": []
}
]
}
]
}
}
there could be n number of children of each object and children can have an array of objects.
What I have done:- I am able to change the very first name but not children name
let resDataArry = [];
let descData: DynamicResource;
response.forEach((x, index) => {
const descName = x.name + ' ' + x.description;
descData = { ...tree.resources[index], name: descName };
resDataArry.push(descData);
});
return resDataArry;
Please help.
You can use nested Array#forEach to access children array and then concatenate the name and description together.
let data = {
"data": {
"type": "AnyType",
"resources": [
{
"id": 1,
"treeId": "1",
"name": "name1",
"description": "description1",
"children": [
{
"id": 3,
"treeId": "1-3",
"name": "subName1",
"description": "subDescription1",
"children": [
{
"id": 6,
"treeId": "1-3-6",
"name": "subSubName1",
"description": "subSubDesc1",
"children": []
}
]
}
]
},
{
"id": 2,
"treeId": "2",
"name": "name2",
"description": "description2",
"children": [
{
"id": 7,
"treeId": "2-7",
"name": "subName2",
"description": "subDescription2",
"children": []
}
]
}
]
}
}
data.data.resources.forEach(function(item){
item.name = item.name + ' ' + item.description;
item.children.forEach(function(child){
child.name = child.name + ' ' + child.description;
});
});
console.log(data);

Array object grouping in JavaScript

I need to convert below unformatted JSON format into formatted input. We need to find id's similar to parent id for different items inside array element of object and then need to push it into children to that id. Below is my code that needs to transform
Input
{
"0": [
{
"id": 10,
"title": "House",
"level": 0,
"children": [],
"parent_id": null
}
],
"1": [
{
"id": 12,
"title": "Red Roof",
"level": 1,
"children": [],
"parent_id": 10
},
{
"id": 18,
"title": "Blue Roof",
"level": 1,
"children": [],
"parent_id": 10
}
],
"2": [
{
"id": 17,
"title": "Blue Windoww",
"level": 2,
"children": [],
"parent_id": 12
},
{
"id": 16,
"title": "Door",
"level": 2,
"children": [],
"parent_id": 13
}
]
}
Output
[
{
"id": 10,
"title": "House",
"level": 0,
"children": [
{
"id": 12,
"title": "RedRoofff",
"level": 1,
"children": [
{
"id": 17,
"title": "Blue Windoww",
"level": 2,
"children": [],
"parent_id": 12
}
],
"parent_id": 10
},
{
"id": 18,
"title": "Blue Roof",
"level": 1,
"children": [],
"parent_id": 10
},
{
"id": 13,
"title": "Wall",
"level": 1,
"children": [
{
"id": 16,
"title": "Door",
"level": 2,
"children": [],
"parent_id": 13
}
],
"parent_id": 10
}
],
"parent_id": null
}
]
Please find the solution to above problem.
first, we track the node with Id and then we update the children array like this.
(btw, your input have a missing node, 13)
const input = {
"0": [{
"id": 10,
"title": "House",
"level": 0,
"children": [],
"parent_id": null
}, {
"id": 13,
"title": "Wall",
"level": 0,
"children": [],
"parent_id": null
}],
"1": [{
"id": 12,
"title": "Red Roof",
"level": 1,
"children": [],
"parent_id": 10
},
{
"id": 18,
"title": "Blue Roof",
"level": 1,
"children": [],
"parent_id": 10
},
],
"2": [{
"id": 17,
"title": "Blue Windoww",
"level": 2,
"children": [],
"parent_id": 12
},
{
"id": 16,
"title": "Door",
"level": 2,
"children": [],
"parent_id": 13
},
]
};
const results = [];
const mapId2Node = Object.values(input).reduce((acc, vals) => {
vals.forEach(val => {
acc[val.id] = val;
if (val.parent_id === null) {
results.push(val);
}
});
return acc;
}, {});
Object.values(input).forEach(vals => {
vals.forEach(val => {
if (val.parent_id !== null) {
mapId2Node[val.parent_id].children.push(val);
}
});
});
conosle.log(results);

Remove element from JSON Object

I have a json array which looks something like this:
{
"id": 1,
"children": [
{
"id": 2,
"children": {
"id": 3,
"children": {
"id": 4,
"children": ""
}
}
},
{
"id": 2,
"children": {
"id": 3,
"children": {
"id": 4,
"children": ""
}
}
},
{
"id": 2,
"children": {
"id": 3,
"children": {
"id": 4,
"children": ""
}
}
},
{
"id": 2,
"children": {
"id": 3,
"children": {
"id": 4,
"children": ""
}
}
},
{
"id": 2,
"children": {
"id": 3,
"children": {
"id": 4,
"children": ""
}
}
},
{
"id": 2,
"children": {
"id": 3,
"children": {
"id": 4,
"children": ""
}
}
},
{
"id": 2,
"children": {
"id": 3,
"children": {
"id": 4,
"children": ""
}
}
}]
}
I would like to have a function which removes the elements which has the "children" empty. How can I do it? I am not asking for the answer, only suggestions
To iterate through the keys of an object, use a for .. in loop:
for (var key in json_obj) {
if (json_obj.hasOwnProperty(key)) {
// do something with `key'
}
}
To test all elements for empty children, you can use a recursive approach: iterate through all elements and recursively test their children too.
Removing a property of an object can be done by using the delete keyword:
var someObj = {
"one": 123,
"two": 345
};
var key = "one";
delete someObj[key];
console.log(someObj); // prints { "two": 345 }
Documentation:
https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Working_with_Objects
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Statements/for...in
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/delete
JSfiddle
function deleteEmpty(obj){
for(var k in obj)
if(k == "children"){
if(obj[k]){
deleteEmpty(obj[k]);
}else{
delete obj.children;
}
}
}
for(var i=0; i< a.children.length; i++){
deleteEmpty(a.children[i])
}

Categories