Cleiton Freitas
Cleiton Freitas

Reputation: 527

Counting all nested objects in array where selected attribute is true

I need to create a recursive function counting objects nested in the array where the selected attribute is true.

var data = [{"id":1,"code":"1","selected":false,"children":[{"id":4,"code":"1.01","selected":false,"children":[{"id":5,"code":"1.01.001","selected":true,"children":[]},{"id":6,"code":"1.01.002","selected":false,"children":[]},{"id":20,"code":"1.01.003","selected":true,"children":[]}]}]},{"id":2,"code":"2","selected":false,"children":[{"id":7,"code":"2.01","selected":false,"children":[{"id":9,"code":"2.01.001","selected":true,"children":[]},{"id":21,"code":"2.01.002","selected":true,"children":[]},{"id":22,"code":"2.01.003","selected":false,"children":[]}]}]},{"id":3,"code":"3","selected":false,"children":[{"id":8,"code":"3.01","selected":false,"children":[{"id":10,"code":"3.01.01","name":"Sementes","selected":false,"children":[{"id":11,"code":"3.01.01.001","selected":true,"children":[]},{"id":23,"code":"3.01.01.002","selected":false,"children":[]},{"id":24,"code":"3.01.01.003","selected":true,"children":[]}]},{"id":25,"code":"3.01.02","selected":false,"children":[{"id":27,"code":"3.01.02.001","selected":true,"children":[]},{"id":28,"code":"3.01.02.002","selected":false,"children":[]},{"id":29,"code":"3.01.02.003","selected":false,"children":[]}]},{"id":26,"code":"3.01.03","selected":false,"children":[{"id":30,"code":"3.01.03.001","selected":true,"children":[]},{"id":31,"code":"3.01.03.002","selected":true,"children":[]},{"id":32,"code":"3.01.03.003","selected":true,"children":[]},{"id":35,"code":"3.01.03.004","selected":false,"children":[]},{"id":34,"code":"3.01.03.005","selected":false,"children":[]}]}]}]}];


const countSelectedChildren = (arr) => {

return arr;
}

console.log(countSelectedChildren(data))

Expected response:

{
    "id": 3,
    "code": "3",
    "selected": false,
    "selectedChildren": 6,
    "children": [
      {
        "id": 8,
        "code": "3.01",
        "selected": false,
        "selectedChildren": 6,
        "children": [
          {
            "id": 10,
            "code": "3.01.01",
            "name": "Sementes",
            "selected": false,
            "selectedChildren": 2,
            "children": [
              {
                "id": 11,
                "code": "3.01.01.001",
                "selected": true,
                "children": []
              },
              {
                "id": 23,
                "code": "3.01.01.002",
                "selected": false,
                "children": []
              },
              {
                "id": 24,
                "code": "3.01.01.003",
                "selected": true,
                "children": []
              }
            ]
          },
          {
            "id": 25,
            "code": "3.01.02",
            "selected": false,
            "selectedChildren": 1,
            "children": [
              {
                "id": 27,
                "code": "3.01.02.001",
                "selected": true,
                "children": []
              },
              {
                "id": 28,
                "code": "3.01.02.002",
                "selected": false,
                "children": []
              },
              {
                "id": 29,
                "code": "3.01.02.003",
                "selected": false,
                "children": []
              }
            ]
          },
          {
            "id": 26,
            "code": "3.01.03",
            "selected": false,
            "selectedChildren": 3,
            "children": [
              {
                "id": 30,
                "code": "3.01.03.001",
                "selected": true,
                "children": []
              },
              {
                "id": 31,
                "code": "3.01.03.002",
                "selected": true,
                "children": []
              },
              {
                "id": 32,
                "code": "3.01.03.003",
                "selected": true,
                "children": []
              },
              {
                "id": 35,
                "code": "3.01.03.004",
                "selected": false,
                "children": []
              },
              {
                "id": 34,
                "code": "3.01.03.005",
                "selected": false,
                "children": []
              }
            ]
          }
        ]
      }
    ]
  }

Can you help me to create this recursive function?

const countSelectedChildren = (arr) => {

return arr;
}

Thanks for your help!

Upvotes: 1

Views: 172

Answers (4)

7HR4IZ3
7HR4IZ3

Reputation: 31

const countSelectedChildren = (data) => {
   let length = 0;
   for (item of data) {
      item.selected && (length++)
      let child_length = countSelectedChildren(item.children || [])[1];
      length += child_length;
      item.selectedChildren = child_length;
   }
   return [data, length];
}

Upvotes: 1

Scott Sauyet
Scott Sauyet

Reputation: 50807

Here's a fairly simple recursion to do this:

const sum = (ns) => ns .reduce ((a, b) => a + b, 0)

const countSelectedChildren = (xs) => 
  xs .map (({children = [], ...rest}, _, __, kids = countSelectedChildren (children)) => ({
    ...rest,
    ...(children .length 
       ? {selectedChildren: sum (kids .map (x => (x .selected ? 1 : 0) + (x .selectedChildren || 0)))}
       : {}
    ),
    children: kids,
  }))

const data = [{"id":1,"code":"1","selected":false,"children":[{"id":4,"code":"1.01","selected":false,"children":[{"id":5,"code":"1.01.001","selected":true,"children":[]},{"id":6,"code":"1.01.002","selected":false,"children":[]},{"id":20,"code":"1.01.003","selected":true,"children":[]}]}]},{"id":2,"code":"2","selected":false,"children":[{"id":7,"code":"2.01","selected":false,"children":[{"id":9,"code":"2.01.001","selected":true,"children":[]},{"id":21,"code":"2.01.002","selected":true,"children":[]},{"id":22,"code":"2.01.003","selected":false,"children":[]}]}]},{"id":3,"code":"3","selected":false,"children":[{"id":8,"code":"3.01","selected":false,"children":[{"id":10,"code":"3.01.01","name":"Sementes","selected":false,"children":[{"id":11,"code":"3.01.01.001","selected":true,"children":[]},{"id":23,"code":"3.01.01.002","selected":false,"children":[]},{"id":24,"code":"3.01.01.003","selected":true,"children":[]}]},{"id":25,"code":"3.01.02","selected":false,"children":[{"id":27,"code":"3.01.02.001","selected":true,"children":[]},{"id":28,"code":"3.01.02.002","selected":false,"children":[]},{"id":29,"code":"3.01.02.003","selected":false,"children":[]}]},{"id":26,"code":"3.01.03","selected":false,"children":[{"id":30,"code":"3.01.03.001","selected":true,"children":[]},{"id":31,"code":"3.01.03.002","selected":true,"children":[]},{"id":32,"code":"3.01.03.003","selected":true,"children":[]},{"id":35,"code":"3.01.03.004","selected":false,"children":[]},{"id":34,"code":"3.01.03.005","selected":false,"children":[]}]}]}]}];

console .log (countSelectedChildren (data))
.as-console-wrapper {max-height: 100% !important; top: 0}

For each element in the input array recur first on any children, then to calculate the selectedChildren for our current node we sum up the results from each of our children, adding one for each if it's selected. Then we simply put back together a new object with selectedChildren included if we have actual children, with children the result of our recursion, and with the rest of the properties from our element.

While we could inline the one call to the sum helper function, it's something we're likely to want fairly often, so it's cleaner to keep it separate.

Upvotes: 2

code_monk
code_monk

Reputation: 10128

I would approach it like this

    const countSelectedChildren = (inArr) => {
        
        const outArr = [];
        
        inArr.forEach(row => {
        
          //  simply count all chilren with selected=true
          row.SelectedChildren = row.children.filter(row => row.selected).length;
        
          //  if there are children, call this function on them
          if (row.children.length) {
             row.children = countSelectedChildren(row.children);
          }
          
          outArr.push(row);
        });
        
        return outArr;
        
    }

const data = [
  {
"id": 1,
"code": "1",
"selected": false,
"children": [
  {
    "id": 4,
    "code": "1.01",
    "selected": false,
    "children": [
      {
        "id": 5,
        "code": "1.01.001",
        "selected": true,
        "children": []
      },
      {
        "id": 6,
        "code": "1.01.002",
        "selected": false,
        "children": []
      },
      {
        "id": 20,
        "code": "1.01.003",
        "selected": true,
        "children": []
      }
    ]
  }
]
  },
  {
"id": 2,
"code": "2",
"selected": false,
"children": [
  {
    "id": 7,
    "code": "2.01",
    "selected": false,
    "children": [
      {
        "id": 9,
        "code": "2.01.001",
        "selected": true,
        "children": []
      },
      {
        "id": 21,
        "code": "2.01.002",
        "selected": true,
        "children": []
      },
      {
        "id": 22,
        "code": "2.01.003",
        "selected": false,
        "children": []
      }
    ]
  }
]
  },
  {
"id": 3,
"code": "3",
"selected": false,
"children": [
  {
    "id": 8,
    "code": "3.01",
    "selected": false,
    "children": [
      {
        "id": 10,
        "code": "3.01.01",
        "name": "Sementes",
        "selected": false,
        "children": [
          {
            "id": 11,
            "code": "3.01.01.001",
            "selected": true,
            "children": []
          },
          {
            "id": 23,
            "code": "3.01.01.002",
            "selected": false,
            "children": []
          },
          {
            "id": 24,
            "code": "3.01.01.003",
            "selected": true,
            "children": []
          }
        ]
      },
      {
        "id": 25,
        "code": "3.01.02",
        "selected": false,
        "children": [
          {
            "id": 27,
            "code": "3.01.02.001",
            "selected": true,
            "children": []
          },
          {
            "id": 28,
            "code": "3.01.02.002",
            "selected": false,
            "children": []
          },
          {
            "id": 29,
            "code": "3.01.02.003",
            "selected": false,
            "children": []
          }
        ]
      },
      {
        "id": 26,
        "code": "3.01.03",
        "selected": false,
        "children": [
          {
            "id": 30,
            "code": "3.01.03.001",
            "selected": true,
            "children": []
          },
          {
            "id": 31,
            "code": "3.01.03.002",
            "selected": true,
            "children": []
          },
          {
            "id": 32,
            "code": "3.01.03.003",
            "selected": true,
            "children": []
          },
          {
            "id": 35,
            "code": "3.01.03.004",
            "selected": false,
            "children": []
          },
          {
            "id": 34,
            "code": "3.01.03.005",
            "selected": false,
            "children": []
          }
        ]
      }
    ]
  }
]
  }
];


const countSelectedChildren = (inArr) => {
    
    const outArr = [];
    
    inArr.forEach(row => {
    
      //  simply count all chilren with selected=true
      row.SelectedChildren = row.children.filter(row => row.selected).length;
    
      //  if there are children, call this function on them
      if (row.children.length) {
         row.children = countSelectedChildren(row.children);
      }
      
      outArr.push(row);
    });
    
    return outArr;
    
}

console.log(countSelectedChildren(data))

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386680

You could take a recursive function for an array and return an object with a counting property and a children array.

const
    addSelected = array => {
        let selectedChildren = 0;
        const
            children = array.map(({ children, ...o }) => {
                if (o.selected) selectedChildren++;
                const temp = addSelected(children);
                selectedChildren += temp.selectedChildren || 0;
                return { ...o, ...temp };
            });

        return children.length
            ? { selectedChildren, children }
            : { children };
    },
    data = [{ id: 1, code: "1", selected: false, children: [{ id: 4, code: "1.01", selected: false, children: [{ id: 5, code: "1.01.001", selected: true, children: [] }, { id: 6, code: "1.01.002", selected: false, children: [] }, { id: 20, code: "1.01.003", selected: true, children: [] }] }] }, { id: 2, code: "2", selected: false, children: [{ id: 7, code: "2.01", selected: false, children: [{ id: 9, code: "2.01.001", selected: true, children: [] }, { id: 21, code: "2.01.002", selected: true, children: [] }, { id: 22, code: "2.01.003", selected: false, children: [] }] }] }, { id: 3, code: "3", selected: false, children: [{ id: 8, code: "3.01", selected: false, children: [{ id: 10, code: "3.01.01", name: "Sementes", selected: false, children: [{ id: 11, code: "3.01.01.001", selected: true, children: [] }, { id: 23, code: "3.01.01.002", selected: false, children: [] }, { id: 24, code: "3.01.01.003", selected: true, children: [] }] }, { id: 25, code: "3.01.02", selected: false, children: [{ id: 27, code: "3.01.02.001", selected: true, children: [] }, { id: 28, code: "3.01.02.002", selected: false, children: [] }, { id: 29, code: "3.01.02.003", selected: false, children: [] }] }, { id: 26, code: "3.01.03", selected: false, children: [{ id: 30, code: "3.01.03.001", selected: true, children: [] }, { id: 31, code: "3.01.03.002", selected: true, children: [] }, { id: 32, code: "3.01.03.003", selected: true, children: [] }, { id: 35, code: "3.01.03.004", selected: false, children: [] }, { id: 34, code: "3.01.03.005", selected: false, children: [] }] }] }] }],
    result = addSelected(data).children;

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 2

Related Questions