Рекурсивная функция возвращает неопределенное значение независимо от достаточного количества операторов возврата - PullRequest
3 голосов
/ 24 марта 2020

Я уже прочитал несколько вопросов и ответов на него. Похоже, что моя рекурсивная функция имеет достаточно выражений "return", поэтому ... я не знаю, почему она возвращает undefined ... Я добавил дополнительный оператор log, чтобы показать, что функция сама находит элемент, но не возвращает его ...

let animals = [
  {
    name: "dogs",
    id: 1,
    children: [
      {
        name: "lessie",
        id: 2
      },
      {
        name: "bark-a-lot",
        id: 3
      }
    ]
  },
  {
    name: "cats",
    id: 4,
    children: [
      {
        name: "meows-a-lot",
        id: 5,
        children: [
          {
            name: "meows-a-lot-in-the-morning",
            id: 6
          }
        ]
      },
      {
        name: "whisk-ass",
        id: 7
      }
    ]
  }
];

function recurseFind(node, id) {
  if (Array.isArray(node)) {
    return node.forEach(el => {
      return recurseFind(el, id);
    });
  } else {
    if (node.id === id) {
      console.log("node matched", node.id, id, node);
      return node;
    } else if (node.children) {
      return node.children.forEach(child => {
        return recurseFind(child, id);
      });
    } else {
      return "not found";
    }
  }
}

const found = recurseFind(animals, 6);
console.log("found", found, "wtf");

Ответы [ 2 ]

5 голосов
/ 24 марта 2020

forEach возвращает undefined, поэтому

return node.forEach(el => {
  return recurseFind(el, id);
});

всегда будет возвращать undefined, независимо от того, что обнаружат рекурсивные вызовы.

Я бы использовал for Вместо этого l oop, и если совпадение найдено, вернуть его:

let animals = [
  {
    name: "dogs",
    id: 1,
    children: [
      {
        name: "lessie",
        id: 2
      },
      {
        name: "bark-a-lot",
        id: 3
      }
    ]
  },
  {
    name: "cats",
    id: 4,
    children: [
      {
        name: "meows-a-lot",
        id: 5,
        children: [
          {
            name: "meows-a-lot-in-the-morning",
            id: 6
          }
        ]
      },
      {
        name: "whisk-ass",
        id: 7
      }
    ]
  }
];

function recurseFind(node, id) {
  if (Array.isArray(node)) {
    for (const el of node) {
      const result = recurseFind(el, id);
      if (result) return result;
    }
  } else {
    if (node.id === id) {
      return node;
    } else if (node.children) {
      for (const child of node.children) {
        const result = recurseFind(child, id);
        if (result) return result;
      }
    }
  }
}

const found = recurseFind(animals, 6) || 'not found';
console.log("found", found);
1 голос
/ 24 марта 2020

VLAZ и CertainPerformance уже указали, почему ваша функция не работает.

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

const recursiveFind = (pred) => (xs) => xs .reduce (
  (r, x) => r != null ? r : pred (x) ? x : recursiveFind (pred) (x.children || []) || null,
  null
)

const findById = (id) => recursiveFind(x => x.id == id)

const animals = [{name: "dogs", id: 1, children: [{name: "lessie", id: 2}, {name: "bark-a-lot", id: 3}]}, {name: "cats", id: 4, children: [{name: "meows-a-lot", id: 5, children: [{ name: "meows-a-lot-in-the-morning", id: 6}]}, {name: "whisk-ass", id: 7}]}];

console .log (findById (3) (animals))
console .log (findById (4) (animals))

Мы начнем с обобщенной функции c, которая ищет объекты, вложенные таким образом, в соответствии с предоставленной им функцией предиката. Затем мы передаем ему предикат x => x.id == id, чтобы создать функцию, которая принимает идентификатор, а затем список значений и находит первое значение с совпадающими идентификаторами в списке, или null, если ничего не найдено.

Если вы абсолютно не используете эту функцию recursiveFind, вы можете вставить ее в findById следующим образом:

const findById = (id, xs) => xs .reduce (
  (r, x) => r != null ? r : x.id == id ? x : findById (id, x.children || []) || null,
  null
)

findById (3, animals)

Но я бы на самом деле предпочел бы go в другом направлении, и сделать это еще более обобщенно c, используя что-то вроде этого:

const recursiveFind = (pred, descend) => (xs) => xs .reduce (
  (r, x) => r != null ? r :  pred (x) ? x : recursiveFind (pred, descend) (descend (x) || []) || null,
  null
)

const findById = (id) => recursiveFind (x => x.id == id, x => x.children)

findById (3) (animals)

Эта версия также параметризует, как мы спускаемся в дочерние узлы. В этом случае мы просто используем x => x.children, но это легко представить, используя другие свойства или более сложный метод.

Во всех них обратите внимание, что функция обрабатывает все узлы вашей структуры вложенного массива, даже когда мы уже нашли совпадение. Если у нас есть, первая проверка (r != null) быстро пропускается, но если производительность критична, вы можете предпочесть решение с явными короткими замыканиями, например, от CertainPerformance.

...