简体   繁体   中英

Most efficient way to way to find deep nested object using index path and update it

I have an array of tree structures. I need to be able to find the object using the index paths and update the toggle boolean. I am able to update the toggle using the index path, but this logic will need to be updated if more levels of nodes are added to the tree. How can I make it generic, so my code still works even if the treeData is changed? It'd be nice if I can set all other nodes false if it is in not in the one of the index paths.

 treeData = [{ name: 'Infiniti', toggle: false, children: [{ name: 'G50', toggle: false, children: [{ name: 'Pure AWD', toggle: false }, { name: 'Luxe', toggle: false }, ], }, { name: 'QX50', toggle: false, children: [{ name: 'Pure AWD', toggle: false }, { name: 'Luxe', toggle: false }, ], }, ], }, { name: 'BMW', toggle: false, children: [{ name: '2 Series', toggle: false, children: [{ name: 'Coupé', toggle: false }, { name: 'Gran Coupé', toggle: false }, ], }, { name: '3 Series', toggle: false, children: [{ name: 'Sedan', toggle: false }, { name: 'PHEV', toggle: false }, ], }, ], }, ]; indexPathArray = ["0/0/1", "1/0/0"] for (const index of indexPathArray) { const indexArray = index.split('/') for (let i = 0; i < indexArray.length; i++) { if (i === 0) { treeData[indexArray[0]].toggle = true; } if (i === 1) { treeData[indexArray[0]].children[indexArray[1]].toggle = true; } if (i === 2) { treeData[indexArray[0]].children[indexArray[1]].children[indexArray[2]].toggle = true; } } } console.log(treeData);

You can achieve it with the code like the following:

for (let path of indexPathArray) {
  let currentNode = { children: treeData };
  for (let i of path.split('/')) {
    currentNode = currentNode.children[i];
    currentNode.toggle = true;
  }
}

The idea here is to have a temporary variable currentNode , which contains the current cursor of the tree traversal.

This will allow you to access next items from the index path, without writing the whole path.

A snippet:

 treeData = [{ name: 'Infiniti', toggle: false, children: [{ name: 'G50', toggle: false, children: [{ name: 'Pure AWD', toggle: false }, { name: 'Luxe', toggle: false }, ], }, { name: 'QX50', toggle: false, children: [{ name: 'Pure AWD', toggle: false }, { name: 'Luxe', toggle: false }, ], }, ], }, { name: 'BMW', toggle: false, children: [{ name: '2 Series', toggle: false, children: [{ name: 'Coupé', toggle: false }, { name: 'Gran Coupé', toggle: false }, ], }, { name: '3 Series', toggle: false, children: [{ name: 'Sedan', toggle: false }, { name: 'PHEV', toggle: false }, ], }, ], }, ]; indexPathArray = ["0/0/1", "1/0/0"] for (let path of indexPathArray) { let currentNode = { children: treeData }; for (let i of path.split('/')) { currentNode = currentNode.children[i]; currentNode.toggle = true; } } console.log(treeData);

With recursion .

Basically, you go in one level at a time and the call the same function as if that inner node is the new root node. Then when you get to the end of your search path, you perform the action you want.

That might look like this:

function togglePath(tree: Tree[], indexPath: string) {
  const [first, ...rest] = indexPath.split('/').map(n => parseInt(n))
  const children = tree[first]?.children

  if (children && rest.length > 0) {
    togglePath(children, rest.join('/'))
  } else {
    tree[first].toggle = true;
  }
}

This is a recursive function because it calls itself.

Each each level it check to see if it's there yet with rest.length > 0 . If there is only one index path then we found the destination, do the toggle.

If we need to drill in more, then call togglePath again with the children of the node from the first index, and provide the rest of the indices for the next level.

This should work at any level:

console.log(treeData[0].children?.[0].children?.[1].toggle) // false
togglePath(treeData, "0/0/1")
console.log(treeData[0].children?.[0].children?.[1].toggle) // true

console.log(treeData[0].children?.[1].toggle) // false
togglePath(treeData, "0/1")
console.log(treeData[0].children?.[1].toggle) // true

See Typescript Playground


I'd also probably pre-parse your index paths to be an array of integers so you don't have to do that string manipulation at each loop.

Maybe like:

function parseIndexPath(indexPath: string): number[] {
  return indexPath.split('/').map(n => parseInt(n))
}

See Playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM