Home > other >  Restructure 2 Arrays with Objects as a Nested Array
Restructure 2 Arrays with Objects as a Nested Array

Time:05-21

The goal is to create a new nested array based on 2 flat arrays with objects. If an id from list B matches a refId in list A, the object is added as a child to the object in list A. This creates a new array 2 levels deep as shown in the example.

However in List B, there are objects that have id's that match refId's of their sibling objects. If such is the case the code should find matches and then add them as children of children of parent object. Thus, 3 levels deep. The code should continue to nest until there are no possible matches.

How can the below code be modified to nest any # of levels deep based matching id's and refId's?

// TOP LEVEL
const listA = [ 
  { 
    "id": 23,
    "refId": 23, 
    "name": 'list A #1',
    "isNested": false,
    "depth": 1,
    "children": []
  }, 
  { 
    "id": 25,
    "refId": 25, 
    "name": 'list A #1',
    "isNested": false,
    "depth": 1,
    "children": []
  }
]
// NO HEIRARCHY
const listB = [ 
  { 
    "id": 23,
    "refId": 1234,
    "name": "test 1",
    "isNested": true, 
    "depth": 2, 
    "children": []
  },
  { 
    "id": 25,
    "refId": 1212,
    "name": "test 1",
    "isNested": true, 
    "depth": 2, 
    "children": []
  },
  { 
    "id": 1234,
    "refId": 4324,
    "depth": 3,
    "name": "test 2",
    "isNested": true, 
    "children": []
  }, 
  { 
    "id": 1234,
    "refId": 5678,
    "depth": 3,
    "name": "test 3",
    "isNested": true, 
    "children": []
  }
]

const nestedArr = listA.map(
  ({ id, name, refId, children }) => {
    return {
      id,
      name,
      refId,
      children: listB.filter((b) => {
        return b.id == refId ? b : ''
      }),
    }
  }
)

console.log(nestedArr)

CodePudding user response:

If your refs are in order, you can do the following:

  • Concat both arrays
  • For every element,
    • Store the ref in a Map so you can easily access it later
    • If id === refId, push it as a top level ref
    • If not, look up its parent and push it to the children array
const refs = new Map();
const nestedArr = [];

for (const ref of listA.concat(listB)) {
  refs.set(ref.refId, ref);
  
  if (ref.id !== ref.refId) {
    refs.get(ref.id).children.push(ref);
  } else {
    nestedArr.push(ref);
  }
}

console.log(nestedArr)

Here's a runnable snippet:

// TOP LEVEL
const listA = [ 
  { 
    "id": 23,
    "refId": 23, 
    "name": 'list A #1',
    "isNested": false,
    "depth": 1,
    "children": []
  }, 
  { 
    "id": 25,
    "refId": 25, 
    "name": 'list A #1',
    "isNested": false,
    "depth": 1,
    "children": []
  }
]
// NO HEIRARCHY
const listB = [ 
  { 
    "id": 23,
    "refId": 1234,
    "name": "test 1",
    "isNested": true, 
    "depth": 2, 
    "children": []
  },
  { 
    "id": 25,
    "refId": 1212,
    "name": "test 1",
    "isNested": true, 
    "depth": 2, 
    "children": []
  },
  { 
    "id": 1234,
    "refId": 4324,
    "depth": 3,
    "name": "test 2",
    "isNested": true, 
    "children": []
  }, 
  { 
    "id": 1234,
    "refId": 5678,
    "depth": 3,
    "name": "test 3",
    "isNested": true, 
    "children": []
  }
];

const refs = new Map();
const nestedArr = [];

for (const ref of listA.concat(listB)) {
  refs.set(ref.refId, ref);
  
  if (ref.id !== ref.refId) {
    refs.get(ref.id).children.push(ref);
  } else {
    nestedArr.push(ref);
  }
}

console.log(nestedArr)

Note: this mutates the original elements

CodePudding user response:

You could create a Map keyed by all refId and relate to them the corresponding node objects, using the Map constructor. Then iterate the second list to make the attachments.

This mutates the existing children arrays, so listA will have the result:

const listA = [{"id": 23,"refId": 23,"name": 'list A #1',"isNested": false,"depth": 1,"children": []},{"id": 25,"refId": 25,"name": 'list A #1',"isNested": false,"depth": 1,"children": []}];
const listB = [{"id": 23,"refId": 1234,"name": "test 1","isNested": true,"depth": 2,"children": []},{"id": 25,"refId": 1212,"name": "test 1","isNested": true,"depth": 2,"children": []},{"id": 1234,"refId": 4324,"depth": 3,"name": "test 2","isNested": true,"children": []},{"id": 1234,"refId": 5678,"depth": 3,"name": "test 3","isNested": true,"children": []}];

const map = new Map(listA.concat(listB).map(node => [node.refId, node]));
for (const {id, refId} of listB) map.get(id).children.push(map.get(refId));

console.log(listA);

If you don't want to mutate the original input, then make sure to create new children arrays:

const listA = [{"id": 23,"refId": 23,"name": 'list A #1',"isNested": false,"depth": 1,"children": []},{"id": 25,"refId": 25,"name": 'list A #1',"isNested": false,"depth": 1,"children": []}];
const listB = [{"id": 23,"refId": 1234,"name": "test 1","isNested": true,"depth": 2,"children": []},{"id": 25,"refId": 1212,"name": "test 1","isNested": true,"depth": 2,"children": []},{"id": 1234,"refId": 4324,"depth": 3,"name": "test 2","isNested": true,"children": []},{"id": 1234,"refId": 5678,"depth": 3,"name": "test 3","isNested": true,"children": []}];

const map = new Map(listA.concat(listB).map(node => [node.refId, {...node, children:[]}]));
for (const {id, refId} of listB) map.get(id).children.push(map.get(refId));

const nestedListA = listA.map(({id}) => map.get(id));
console.log(nestedListA);

  • Related