Recursive function returns undefined regardless of enough return statements

有些话、适合烂在心里 提交于 2021-01-27 14:50:26

问题


I have read a few questions and answers on it already. It looks like my recursive function has got enough "return" statements, so... I do not know why it returns undefined... I have added extra log statement to show that the function itself finds the element, but does not return it...

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

回答1:


forEach returns undefined, so

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

will always return undefined, no matter what the recursive calls find.

I'd use a for loop instead, and if a match is found, return it:

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



回答2:


VLAZ and CertainPerformance already pointed out why your function wasn't working.

Here's an alternative technique that seems a little simpler to me:

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

We start with a generic function that searches for objects nested this way by whether they match the supplied predicate function. Then we pass it the predicate x => x.id == id to create a function that takes an id and then a list of values and finds the first value with matching ids in the list, or null if none are found.

If you have absolutely no use for this recursiveFind function, you can inline it into findById like this:

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

findById (3, animals)

But I actually would prefer to go in the other direction, and make it still more generic, using something like this:

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)

This version also parameterizes how we descend into the children of a node. In this case, we simply use x => x.children, but it's easy to imagine using other properties or a more complex method.

In all of these, do note that the function processes all nodes of your nested array structure, even when we've already found a match. If we have, the first check (r != null) skips ahead quickly, but if performance is critical, you might prefer a solution with explicit short-circuiting loops such as the one from CertainPerformance.



来源:https://stackoverflow.com/questions/60830333/recursive-function-returns-undefined-regardless-of-enough-return-statements

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!