Home > other >  Loop through an array of objects and update parent object count if child object exists
Loop through an array of objects and update parent object count if child object exists

Time:11-08

I am using Angular 13 and I have an array of objects like this:

   [{
        "name": "Operating System",
        "checkedCount": 0,
        "children": [{
                "name": "Linux",
                "value": "Redhat",
                "checked": true
            },
            {
                "name": "Windows",
                "value": "Windows 10"
            }
        ]
    },
    {
        "name": "Software",
        "checkedCount": 0,
        "children": [{
                "name": "Photoshop",
                "value": "PS",
                "checked": true
            },
            {
                "name": "Dreamweaver",
                "value": "DW"
            },
            {
                "name": "Fireworks",
                "value": "FW",
                "checked": true
            }
        ]
    }
]

I would like to loop through the array, check if each object has a children array and it in turn has a checked property which is set to true, then I should update the checkedCount in the parent object. So, result should be like this:

    [{
        "name": "Operating System",
        "checkedCount": 1,
        "children": [{
                "name": "Linux",
                "value": "Redhat",
                "checked": true
            },
            {
                "name": "Windows",
                "value": "Windows 10"
            }
        ]
    },
    {
        "name": "Software",
        "checkedCount": 2,
        "children": [{
                "name": "Photoshop",
                "value": "PS",
                "checked": true
            },
            {
                "name": "Dreamweaver",
                "value": "DW"
            },
            {
                "name": "Fireworks",
                "value": "FW",
                "checked": true
            }
        ]
    }
]

I tried to do it this way in angular, but this is in-efficient and results in an error saying this.allFilters[i].children[j] may be undefined. So, looking for an efficient manner to do this.

  for(let j=0;i<this.allFilters[i].children.length; j  ) {
    if (Object.keys(this.allFilters[i].children[j]).length > 0) {
      if (Object.prototype.hasOwnProperty.call(this.allFilters[i].children[j], 'checked')) {
        if(this.allFilters[i].children[j].checked) {
          this.allFilters[i].checkedCount  ;
        }
      }
    }
  }

CodePudding user response:

Use a nested for loop to check all the children. If checked is truthy, increment the count of the parent. You don't need to check if parent.children has any elements since if there are no elements the loop won't run anyways.

// minified data
const data = [{"name":"Operating System","checkedCount":0,"children":[{"name":"Linux","value":"Redhat","checked":!0},{"name":"Windows","value":"Windows 10"}]},{"name":"Software","checkedCount":0,"children":[{"name":"Photoshop","value":"PS","checked":!0},{"name":"Dreamweaver","value":"DW"},{"name":"Fireworks","value":"FW","checked":!0}]}];

for (const parent of data) {
    for (const child of parent.children) {
        if (child.checked) parent.checkedCount  ;
    }
}

console.log(data);

CodePudding user response:

You want to iterate over the items in an array, so let's use Array#forEach and, for each item in the array, you want to update some property.

const input = []; // Your data here.

input.forEach(item => {
  item.checkedCount = // ...
});

It looks like you want to aggregate the children array into an integer by summing the number of checked properties set to true. Since we're aggregating on an array, let's use Array#reduce. The function will take the value of the aggregation up to the current iteration (agg), the item for the current iteration (cur), and a default value for agg (0).

const input = []; // Your data here.

input.forEach(item => {
  item.checkedCount = item.children.reduce((agg, cur) => {
    // ...
  }, 0);
});

Each iteration, we'll add to the aggregated value (agg) if the current item's checked field exists and is true.

const input = []; // Your data here.

input.forEach(item => {
  item.checkedCount = item.children.reduce((agg, cur) => {
    if (cur.checked) {
      return agg   1;
    } else {
      return agg;
    }
  }, 0);
});

Putting it together and cleaning it up:

const input = [{name:"Operating System",checkedCount:NaN,children:[{name:"Linux",value:"Redhat",checked:!0},{name:"Windows",value:"Windows 10"}]},{name:"Software",checkedCount:NaN,children:[{name:"Photoshop",value:"PS",checked:!0},{name:"Dreamweaver",value:"DW"},{name:"Fireworks",value:"FW",checked:!0}]}];

input.forEach(item => {
  item.checkedCount = item.children.reduce((agg, cur) => {
    return cur.checked ? agg   1 : agg;
  }, 0);
});

console.dir(input);

CodePudding user response:

Using filter length on children array should do the job:

const data = [{"name":"Operating System","checkedCount":null,"children":[{"name":"Linux","value":"Redhat","checked":true},{"name":"Windows","value":"Windows 10"}]},{"name":"Software","checkedCount":null,"children":[{"name":"Photoshop","value":"PS","checked":true},{"name":"Dreamweaver","value":"DW"},{"name":"Fireworks","value":"FW","checked":true}]}];

data.forEach(itm => {
  itm.checkedCount = itm.children?.filter(e => e.checked === true).length ?? 0;
});

console.log(input);

CodePudding user response:

No need to complicate it like that, you just need to check checked property in children.

data.forEach((v) => {
  v.children.forEach((child) => {
    if (child.checked) {
      v.checkedCount  ;
    }
  });
});

CodePudding user response:

I would suggest going functional.

Using map

const children = arr.map(obj => obj.children);

const result = children.map((child, idx) => {
    const checkedCount = child.filter(obj => obj.checked)?.length;

    return { 
        ...arr[idx], 
        checkedCount 
    };
});

console.log(result)

or using forEach

const result = [];

const children = arr.map(obj => obj.children);
children.forEach((child, idx) => {
    const checkedCount = child.filter(obj => obj.checked)?.length;

    result[idx] = {
        ...arr[idx],
        checkedCount
    };
});

console.log(result)
  • Related