Skip to content Skip to sidebar Skip to footer

Recursive Function Returns Undefined Regardless Of Enough Return Statements

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 hav

Solution 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
      }
    ]
  }
];

functionrecurseFind(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;
    } elseif (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);

Solution 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:

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

constfindById = (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:

constfindById = (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:

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

constfindById = (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.

Post a Comment for "Recursive Function Returns Undefined Regardless Of Enough Return Statements"