Estoy tratando de encontrar un Objeto específico en un Objeto anidado por id y escribí esta función, que funciona de maravilla:
const findNestedObjById = (tree, myFunction, id) => { if(tree.attributes.node_id === id){ myFunction(tree) } else{ if(tree.children){ tree.children.forEach(child => { findNestedObjById(child, myFunction, id) }); } } }; const doThat = (tree) => { console.log("Got it: " + tree.name) } findNestedObjById(myObj, doThat, "0.1.2.1");Pero quiero poder obtener la "ruta" del objeto (por ejemplo, myObj.children[0].children[2]) (La propiedad de los niños de mi objeto es una matriz) Así que quería reescribir la función usando un fori bucle en lugar de un foreach, para que luego pudiera agregar el índice de la matriz (guardado en i del bucle fori en ese momento) a una cadena de ruta.
Así que quería empezar con esta función:
const findWithFori = (tree, myFunction, id) => { if(tree.attributes.node_id === id){ myFunction(tree) } else{ if(tree.children){ for (let i = 0; i < tree.length; i++) { const child = tree.children[i]; findNestedObjById(child, myFunction, id) } } } };Pero no funciona, puede ubicar el objeto por id, si myObj inicial ya tiene la id correcta, pero no encuentra objetos anidados, como lo hace la primera función y no entiendo por qué.
Si ayuda a responder la pregunta, myObj se ve así por cierto:
const myObj = { name: "Mein zweiter Baum", attributes: { node_id: "0" }, children: [ { name: "Lorem", attributes: { node_id: "0.1", done: true }, children: [ { name: "Ipsum", attributes: { node_id: "0.1.1", done: true }, children: [ { name: "Dolor", attributes: { node_id: "0.1.1.1", done: false } } ] }, { name: "Sit", attributes: { node_id: "0.1.2", done: false }, children: [ { name: "Anet", attributes: { node_id: "0.1.2.1" } } ] } ] } ] };Podrías devolver los índices.
Si se encuentra un elemento, devuelve una matriz vacía o undefined . Dentro de some , obtenga el resultado de los niños y, si no está indefinido, agregue el índice real delante de la matriz.
const findNestedObjById = (tree, id, callback) => { if (tree.attributes.node_id === id) { callback(tree); return []; } if (tree.children) { let path; tree.children.some((child, index) => { path = findNestedObjById(child, id, callback); if (path) { path.unshift(index); return true; } }); return path; } }, doThat = tree => { console.log("Got it: " + tree.name); }, data = { name: "Mein zweiter Baum", attributes: { node_id: "0" }, children: [{ name: "Lorem", attributes: { node_id: "0.1", done: true }, children: [{ name: "Ipsum", attributes: { node_id: "0.1.1", done: true }, children: [{ name: "Dolor", attributes: { node_id: "0.1.1.1", done: false } }] }, { name: "Sit", attributes: { node_id: "0.1.2", done: false }, children: [{ name: "Anet", attributes: { node_id: "0.1.2.1" } }] }] }] } console.log(findNestedObjById(data, "0.1.2.1", doThat)); // [0, 1, 0] .as-console-wrapper { max-height: 100% !important; top: 0; }Haría esto construyendo sobre algunas funciones reutilizables. Podemos escribir una función que visite un nodo y luego visite recursivamente todos los nodos de sus children . Sin embargo, para usar esto para un find , queremos poder detenernos una vez que se encuentra, por lo que una función generadora tendría sentido aquí. Podemos extender una versión básica de este 1 para permitir que cada parada incluya no solo los valores, sino también sus rutas.
Luego podemos superponer una función genérica de búsqueda de ruta por predicado, probando cada nodo que genera hasta que uno coincida con el predicado.
Finalmente, podemos escribir fácilmente una función usando this para buscar por node_id . Podría verse así:
function * visit (value, path = []) { yield {value, path} for (let i = 0; i < (value .children || []) .length; i ++) { yield * visit (value .children [i], path .concat (i)) } } const findDeepPath = (fn) => (obj) => { for (let o of visit (obj)) { if (fn (o .value)) {return o .path} } } const findPathByNodeId = (id) => findDeepPath (({attributes: {node_id}}) => node_id === id) const myObj = {name: "Mein zweiter Baum", attributes: {node_id: "0"}, children: [{name: "Lorem", attributes: {node_id: "0.1", done: true}, children: [{name: "Ipsum", attributes: {node_id: "0.1.1", done: true}, children: [{name: "Dolor", attributes: {node_id: "0.1.1.1", done: false}}]}, {name: "Sit", attributes: {node_id: "0.1.2", done: false}, children: [{name: "Anet", attributes: {node_id: "0.1.2.1"}}]}]}]} console .log (findPathByNodeId ('0.1.2.1') (myObj)) //=> [0, 1, 0]Si queremos devolver el nodo y la ruta, simplemente es cuestión de reemplazar
if (fn (o .value)) {return o .path}con
if (fn (o .value)) {return o}y obtendríamos algo como:
{ value: {attributes: {node_id: "0.1.2.1"}, name: "Anet"}, path: [0, 1, 0], }1 Una versión básica para nodos sin sus rutas podría verse así:
function * visit (obj) { yield obj for (let child of (obj .children || [])) { yield * visit (child) } }y podríamos escribir una búsqueda genérica de valores que coincidan con un predicado con
const findDeep = (fn) => (obj) => { for (let o of visit (obj)) { if (fn (o)) {return o} } }Las capas en el manejo de la ruta agregan algo de complejidad, pero no mucha.