2

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)

2 Answers 2

2

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

Sign up to request clarification or add additional context in comments.

Comments

0

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);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.