skip to Main Content

I have an array of nested objects:

const members = [
  {
    name: 'Mike',
    type: 'MANAGER',
    children: []
  },
  {
    name: 'John',
    type: 'MANAGER',
    children: [
      {
        name: 'George',
        type: 'EMPLOYEE',
        children: []
      },
      {
        name: 'Jake',
        type: 'EMPLOYEE',
        children: []
      },
      {
        name: 'Fred',
        type: 'MANAGER',
        children: [
          {
            name: 'Henry',
            type: 'EMPLOYEE',
            children: []
          },
          {
            name: 'Julie',
            type: 'MANAGER',
            children: []
          }
        ]
      }
    ]
  },
  {
    name: 'Alex',
    type: 'MANAGER',
    children: [
      {
        name: 'Mark',
        type: 'EMPLOYEE',
        children: []
      },
      {
        name: 'Ashley',
        type: 'MANAGER',
        children: []
      }
    ]
  }
];

How can I recursively remove all of the EMPLOYEE objects from the array and add them to a new array?

I’m able to add all of the EMPLOYEE members to a new array, but I’m not sure how to remove them from the original array.

const employees = [];

const extractEmployees = (members) => {
  for(let i = 0; i < members.length; i++) {
    const member = members[i];

    // add employee to employees array
    if(member.type === 'EMPLOYEE') {
      employees.push(member);
    }

    if(member.children) {
      // call function recursively for member children
      extractEmployees(member.children);
    }
  }
}

// initialize
extractEmployees(members);

2

Answers


  1. The extractEmployees can return a new array of members (newMembers) that doesn’t include the employees. This array is assigned to the member.children:

    const employees = [];
    
    const extractEmployees = (members) => {
      const newMembers = []; // init a new array
    
      for(let i = 0; i < members.length; i++) {
        const member = members[i];
    
        if(member.type === 'EMPLOYEE') {
          employees.push(member);
        } else { // if not employee add it to newMembers
          newMembers.push(member);
        }
    
        if(member.children) {
          // assign the returned array of members to current children
          member.children = extractEmployees(member.children);
        }
      }
      
      return newMembers; // return the new array of members
    }
    
    const members = [{"name":"Mike","type":"MANAGER","children":[]},{"name":"John","type":"MANAGER","children":[{"name":"George","type":"EMPLOYEE","children":[]},{"name":"Jake","type":"EMPLOYEE","children":[]},{"name":"Fred","type":"MANAGER","children":[{"name":"Henry","type":"EMPLOYEE","children":[]},{"name":"Julie","type":"MANAGER","children":[]}]}]},{"name":"Alex","type":"MANAGER","children":[{"name":"Mark","type":"EMPLOYEE","children":[]},{"name":"Ashley","type":"MANAGER","children":[]}]}];
    
    // initialize
    extractEmployees(members);
    
    console.log(members);

    The previous solution replaces the original children arrays. If you want to keep the original arrays, you can save the items in another array, empty the original members and add to it non-employees:

    const employees = [];
    
    const extractEmployees = (members) => {
      const prevMembers = [...members]; // clone members
      members.length = 0; // empty the original array
    
      // iterate the previous members
      for(let i = 0; i < prevMembers.length; i++) {
        const member = prevMembers[i];
    
        if(member.type === 'EMPLOYEE') {
          employees.push(member);
        } else { // if not employee add to the original members array
          members.push(member);
        }
    
        if(member.children) {
          extractEmployees(member.children);
        }
      }
    }
    
    const members = [{"name":"Mike","type":"MANAGER","children":[]},{"name":"John","type":"MANAGER","children":[{"name":"George","type":"EMPLOYEE","children":[]},{"name":"Jake","type":"EMPLOYEE","children":[]},{"name":"Fred","type":"MANAGER","children":[{"name":"Henry","type":"EMPLOYEE","children":[]},{"name":"Julie","type":"MANAGER","children":[]}]}]},{"name":"Alex","type":"MANAGER","children":[{"name":"Mark","type":"EMPLOYEE","children":[]},{"name":"Ashley","type":"MANAGER","children":[]}]}];
    
    // initialize
    extractEmployees(members);
    
    console.log(members);
    Login or Signup to reply.
  2. It is probably better practice to not collect the employees in a global variable. One way to collect them is to yield them from a generator.

    The example in the question had no employees that had "children". If that is a possible scenario, then you might want to collect those employee records without those children, as those could also have employees which would be collected separately, leading to some duplication.

    Finally, to filter inplace, you could use the two-index approach where items are packed at the left side of the array (when they should not be removed) and then the array length can be adapted accordingly.

    Here is code that implements those ideas:

    function* extractEmployees(members) {
        let length = 0;
        for (const member of members) {
            const { children, ...rest } = member; 
            if (member.type === 'EMPLOYEE') yield rest; // Without children
            else members[length++] = member;
            if (children) yield* extractEmployees(children);
        }
        members.length = length;
    }
    
    // Example call
    const members = [{"name":"Mike","type":"MANAGER","children":[]},{"name":"John","type":"MANAGER","children":[{"name":"George","type":"EMPLOYEE","children":[]},{"name":"Jake","type":"EMPLOYEE","children":[]},{"name":"Fred","type":"MANAGER","children":[{"name":"Henry","type":"EMPLOYEE","children":[]},{"name":"Julie","type":"MANAGER","children":[]}]}]},{"name":"Alex","type":"MANAGER","children":[{"name":"Mark","type":"EMPLOYEE","children":[]},{"name":"Ashley","type":"MANAGER","children":[]}]}];
    
    const employees = [...extractEmployees(members)];
    
    console.log("=== members with employees removed ===");
    console.log(members);
    console.log("=== the removed employees ===");
    console.log(employees);

    If you really want to collect the unaltered employees (even with children), then do yield member instead of yield rest.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search