Рекурсивный цикл по массиву объектов с использованием цикла for - PullRequest
0 голосов
/ 06 декабря 2018

TLDR; Если у меня есть массив, содержащий объекты, и некоторые из этих объектов являются массивами, которые содержат больше объектов, как я могу рекурсивно выполнить цикл по ним и вернуть одно из вложенных значений?С моим кодом ниже, использование return анализирует только первый элемент.Если я не использую return, значение теряется, потому что оно уничтожается в одной из областей рекурсии.

var currentNode = [{...}, {...}, { items: [{...}, {...}] }]    

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

Более подробное описание:

Я сейчас имею делос древовидной структурой, которая в настоящее время сохраняется в состоянии Vuex.

Прежде чем я приступлю к проблеме, у меня есть два вопроса:

  1. Есть ли способ решить лексическую область видимости?проблема, с которой я сталкиваюсь?
  2. Если нет, как звучит мое альтернативное предлагаемое решение?

Кроме того, для краткости, у меня есть копия демонстрационной версии JSFiddle здесь .

С попыткой решения, приведенной ниже, у меня возникли проблемы с лексическим ограничением в моей функции рекурсии (в частности, в последнем цикле for of.

Но позвольте мне начать с описаниядерево. У него есть два вида узлов.

individual node, который выглядит следующим образом:

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

и wrapper node. Дочерние элементы в wrapper node могут бытьindividual node или wrapper node.

Его структура выглядит следующим образом:

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

ThesУзлы также могут быть вложены бесконечное число раз.

Вот пример объекта:

{
    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`
                }
                ...
            ]
        }
        ...
    ]
}

С этим деревом я хотел бы иметь возможность добавлять individual nodes и wrapper nodes где-нибудь в дереве, но у меня возникают проблемы с этим.

Моя первая попытка состояла в том, чтобы обойти каждый узел и присвоить ему UUID.План состоял в том, чтобы пройтись по дереву, и когда я нашел соответствующий UUID, я мог манипулировать им по мере необходимости.

Вот как выглядел код для этой попытки:

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

Можно ли получить приведенный выше код работает?В частности, цикл через дочерние элементы узла-обертки?

Если нет, моя идея сейчас состоит в том, чтобы вместо того, чтобы просто иметь дерево в состоянии, иметь три вещи.

  1. nodeTree - объект, имеющий ту же форму, что и исходные данные, но каждый узел представляет собой только UUID
  2. allRuleUuids - массив строк.Каждая строка представляет собой UUID узла
  3. nodeDataByUuid - Объект, который связан с UUID в массиве allRuleUuids.Каждый объект будет содержать данные для каждого узла.

Мне нужно поддерживать следующее поведение:

  • Добавление individual node и wrapper node в любом местеtree.
  • Удаление individual node и wrapper node (включая всех его дочерних элементов) из любой части дерева.

Заранее спасибо за помощь!

Ответы [ 4 ]

0 голосов
/ 06 декабря 2018

Я выяснил проблему, которая у меня была!Мне пришлось присвоить значение временной переменной, а затем вернуть его, если оно не было неопределенным.

  if (Array.isArray(tree)) {
    for (let item of tree) {
      let tmp = searchTreeForNode(item, nodeUuid);
      if (tmp !== undefined) {
        return tmp;
      }
    }
  }
0 голосов
/ 06 декабря 2018

Вы можете использовать массив индексов для представления пути к узлу, например:

[0, 1, 4]

Хорошо, это слишком упрощенная реализация.

function getNode(tree, path) {
    let current = tree;

    // find node
    for (let i = 0; i < path.length; i++) {
        current = current.children[path[i]];
    }

    return current;
}

function addNode(tree, path, node) {
    const index = path.pop();
    // ^ careful, this mutates the original array
    // you can clone it if you wish
    // or use string paths like `0/1/4`
    // and use path.split('/') to get the indexes

    const parent = getNode(tree, path);

    parent.children[index] = node;
}

function deleteNode(tree, path) {
    const index = path.pop();
    const parent = getNode(tree, path);

    delete parent.children[index];
}

// you can get nodes like this
console.log(getNode(tree, [1]));

// and add nodes like this
addNode(tree, [0, 3], { name: 'test', value: 'test' });

// you can get the root node like this
console.log(getNode(tree, []));

Я не обработалслучай, когда указан неверный путь, то есть предполагается, что все индексы в пути [0, 1, 4] относятся к узлам-оберткам, кроме последнего индекса.Но вы поняли.

0 голосов
/ 06 декабря 2018

Я изменил твой код.таким образом, он будет работать

https://jsfiddle.net/v2rkj376/1/

function searchTreeForNode(currentNode, nodeUuidToFind) {
  if (currentNode.uuid === nodeUuidToFind) {
    return currentNode.name || 'untitled ('+currentNode.uuid+')';
  }

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

более чистая альтернатива - просто сплющить структуру и затем сделать простую находку.

const flattenTree = node => 
    node.children 
        ? [node, ...node.children.flatMap(flattenTree)] 
        : [node];

тогда нахождение становится просто:

flattenTree(sampleTree).find(node=>node.uuid === 'item5');

https://jsfiddle.net/3dzbto8n/

0 голосов
/ 06 декабря 2018

Я думаю, вам нужно что-то вроде этого:

    currentNode.forEach( singleNode => {
      evalueateChildNodes(singleNode);
    });

 evaluateChildNodes(node) {

 // You can check the id here and assign it to some variable if necessary
 // Or if you found your specific node, you can push a new node to its children etc.
 // Note that even though the function name is 'evaluateChildNodes',
 // it will touch all root nodes due to the forEach() above.

    if (node.children) {
      evaluateChildNodes(node.children);
    }
  }

Этот код будет рекурсивно проходить по всему дереву и касаться каждого узла.

В качестве альтернативы, вы можете назначить вышеуказанное переменнойи просто возвращайте узел всякий раз, когда вы нашли что-то подходящее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...