Tengo una estructura de datos anidada y quiero crear una función recursiva que, dado el parámetro de nombre de un objeto, devolverá el parámetro de nombre del objeto principal.
Hay varias preguntas relacionadas, sin embargo, las respuestas no explican por qué mi función getParentName no funciona.
¿Por qué getParentName no funciona?
const nestedData = { name: "parent", children: [{ name: "child", children: [{ name: "grandchild" }] }], }; function getParentName(nested, name) { if (nested.children && nested.children.map((d) => d.name).includes(name)) { return nested.name; } else if (nested.children) { nested.children.forEach((child) => { return getParentName(child, name); }); } return undefined; //if not found } //The parent of "grandchild" is "child" - but the function returns undefined const parentName = getParentName(nestedData, "grandchild");¿Por qué esta función no encuentra al padre?
El problema con su respuesta es .forEach ignora el valor de return . No hay return para su else if sucursal. .forEach es solo para efectos secundarios. Considere usar un generador que facilite la expresión de su solución:
function* getParentName({ name, children = [] }, query) { for (const child of children) if (child.name === query) yield name else yield *getParentName(child, query) } const data = { name: "parent", children: [{ name: "child", children: [{ name: "grandchild" }] }], } const [result1] = getParentName(data, "grandchild") const [result2] = getParentName(data, "foobar") const [result3] = getParentName(data, "parent") console.log("result1", result1) console.log("result2", result2) console.log("result3", result3) La respuesta será undefined si no se encuentra ningún nodo coincidente o si un nodo coincidente no tiene un padre:
result1 child result2 undefined result3 undefined Se necesita el aviso [] para capturar un solo resultado. Esto se debe a que los generadores pueden devolver 1 o más valores. Si no le gusta esta sintaxis, puede escribir una first función genérica que obtenga solo el primer valor del generador:
function first(it) { for (const v of it) return v } const result1 = first(getParentName(data, "grandchild")) const result2 = first(getParentName(data, "foobar")) const result3 = first(getParentName(data, "parent")) Las ventajas de este enfoque son numerosas. Su intento usa .map e .includes , los cuales iteran completamente a través de children . En la otra rama, se usa .forEach , que también itera exhaustivamente a través de todos children elementos secundarios. Este enfoque evita .map e .includes innecesarios, pero también se detiene inmediatamente después de leer el primer valor.
@Mulan respondió a mi pregunta, afirmando que la función falló porque se ignoran las declaraciones de retorno dentro de .forEach(). Luego ofrecieron una función de generador como alternativa superior.
En aras de la claridad de la comparación, aquí hay una forma mínimamente alterada de la función original que funciona. forEach() se reemplazó con un bucle (for x of array). Además, solo se devuelven valores veraces.
function getParentName(nested, name) { if (nested.children && nested.children.some((d) => d.name === id)) { return nested.name; } else if (nested.children) { for (const child of node.children) { const result = getParentName(child, id); if (result) return result; } } return undefined; //if not found }