[Solved] Recursively looping over an array of objects using a for loop (I'm having lexical scoping problems)

vuex

#1

I posted the solution to my problem below

Hello, I currently dealing with a tree-like structure that’s currently saved in a Vuex state.

Before I jump into the problem, I have two questions:

  1. Is there a way to solve the lexical scoping problem I’m running into?
  2. If not, how does my alternative proposed solution sound?

With the attempted solution below, I’m having trouble with lexical scoping in my recursion function. But let me start by describing the tree. It has two different kinds of nodes.

An individual node which looks like this:

{
    name: `string`
    value: `string`
}

And a wrapper node. The children in the wrapper node can be an individual node or wrapper node.

Its structure looks like this:

{
    type: `string`,
    children: [
        {},
        {},
        ...
    ]
}

These nodes can also be nested an infinite number of times.

Here’s an example object:

{
    type: `level 1`,
    children: [
        {
            type: `level 2`,
            children: [
                {
                    type: `level 3`,
                    children: []
                },
                {
                    name: `item 1`,
                    value: `value 1`
                },
                {
                    name: `item 2`,
                    value: `value 2`
                },
                ...
            ]
        },
        {
            name: `item 1`,
            value: `value 1`
        },
        {
            type: `level 2.1`,
            children: [
                {
                    name: `item 3`,
                    value: `value 3`
                }
                ...
            ]
        }
        ...
    ]
}

With this tree, I would like to be able to add individual nodes and wrapper nodes anywhere in the tree, but I’m having trouble doing this.

My initial attempt involved traversing every node and assigning it a UUID. The plan was to loop through the tree and when I found a matching UUID, I could manipulate it as needed.

This is what the code looked like for that attempt:

// This starts with the top-level wrapper node
function searchTreeForNode(currentNode, nodeUuidToFind) {
  if (currentNode.uuid === nodeUuidToFind) {
    return currentNode;
  }

  // If it's a wrapper node, parse the children
  if (currentNode.hasOwnProperty("type")) {
    return searchTreeForNode(currentNode.children, nodeUuidToFind);
  }

  // If it's the contents of a wrapper node, see if that node lives in them
  if (Array.isArray(currentNode)) {
    let resolvedUuids = [];
    for (let node of currentNode) {
      resolvedUuids.push(node.uuid);
    }

    // If found, return the node
    let uuidLocation = resolvedUuids.indexOf(nodeUuidToFind);
    if (uuidLocation !== -1) {
      return currentNode[uuidLocation];
    }

    // If it's not found, looping through the children (there should be more wrapper nodes in the children)
    for (let node of currentNode) {
      // THIS CAN'T LOOP
      // BUT there's a lexical scoping problem if I don't use `return`
      return searchTreeForNode(node, nodeUuidToFind);
    }
  }
}

Is it possible to get the above code working? Specificially the looping through the children of the wrapper node?

If not, my idea right now is instead of just having the tree in the state, to have three things.

  1. nodeTree - An object which has the same shape as the original data, but each node is only a UUID
  2. allRuleUuids - An array of strings. Each string is the UUID of a node
  3. nodeDataByUuid - An object which is keyed to the UUIDs in the allRuleUuids array. Each object would contain the data for each node.

The behavior I need to support is the following:

  • Adding an individual node and wrapper node anywhere in the tree
  • Deleting an individual node and wrapper node (including all of it’s children) from the tree

I have a copy of a JSFiddle demo here.

Thank you in advance for your help!


#2

I figured out the solution to my problem.

To correctly return a value in the for loop, my code had to look like this:

  if (Array.isArray(tree)) {
    for (let item of tree) {
      let tmp = searchTreeForNode(item, nodeUuid);
      if (tmp !== undefined) {
        return tmp;
      }
    }
  }