Tengo una tienda Redux que actualmente me está devolviendo una lista de cadenas que representan el ejemplo de carpetas a continuación"
/Documents/Pictures/Test
/Documents/Pictures/Test2
/System
/Libraries/Node
y me gustaría cubrir esto con un objeto dict/json en javascript para que se vea así:
data = { id:'0', name:"/", child:[{ id:'1', name:"Documents", child:[ id:'2', name:"Pictures", child:[{ id:'3', name:"Test", child:[] }, { id:'3', name:"Test2", child:[] }] ]}, id:'1', name:"System", child:[] ]} .... so on and so forthPasé dos días en esto, y jugué con el uso de Object.keys, pero parece que no puedo hacer que funcione y hacer coincidir, pero parece que no puedo hacer que funcione.
este es mi código actual que "funciona" pero no lo hace de forma recursiva
convertThePaths(){ var pathRow = this.props.paths[0]; //grabbing the first array to test (/documents/pictures/test) is in here pathArray = pathRow.split("/") //splits between the /'s to get folder names var data = { id:'0',name:"/",child:[]} for (var j = 0; pathArray.length;j++){ data.child.push( id: j name: pathArray[j], child: [] // need recursive here? maybe call convertPaths? ) }la identificación no importa siempre que sea única, sé cómo hacer esto en python pero no en javascript. cualquier ayuda es apreciada! Siento que estoy complicando demasiado esto...
A continuación se explica cómo hacerlo. Lo explico con comentarios de código.
const convertThePaths = (pathRows,data,index)=>{ // Once index is the size of the pathRows were done if (pathRows.length===index){ return; } let pathArray = pathRows[index].split("/"); //splits between the /'s to get folder names pathArray = pathArray.slice(1,pathArray.length); // remove empty string at start of arr. // Cur depth of dictionary let curData = data; // Loop through all paths in pathArray for (const pathName of pathArray){ // Filter for the pathName in the children let childInlst = curData["child"].filter(obj=>obj.name===pathName); // If list is empty no such child was found so add it if (childInlst.length===0){ // Add child that doesn't exist yet curData["child"].push({ "name":pathName, "id":`${parseInt(curData["id"])+1}`, "child":[], }) } // Now child must exist so filter it and it will be index 0 of the list curData = curData["child"].filter(obj=>obj.name===pathName)[0]; // Then repeat } // Now repeat the same step with the next path in pathRows convertThePaths(pathRows,data,index+1); } // Prints out the data recursively const printData = (data)=>{ console.log(data.name+" "+data.id); if (data.child.length===0){ return; } for (const child of data.child){ printData(child) } } const Main=()=>{ const pathRows = [ "/Documents/Pictures/Test", "/Documents/Pictures/Test2", "/System", "/Libraries/Node", ] // Data start state const data = {"id":"0","name":"/","child":[]}; // Recursive function convertThePaths(pathRows,data,0); // function to print data out recursively printData(data); } Main();Salida impresa recursivamente
/ 0 Documents 1 Pictures 2 Test 3 Test2 3 System 1 Libraries 1 Node 2Aquí hay un enfoque basado en una función bastante genérica que he usado en otros lugares :
const setPath = ([p, ...ps], id = 0) => (v) => (o) => p == undefined ? v : Object .assign ( Array .isArray (o) || Number .isInteger (p) ? [] : {}, {...o, [p]: setPath (ps, id + 1) (v) ((o || {}) [p])}, ) const expand = (o, id = 0) => Object .entries (o) .map (([name, v]) => ({id, name, child: expand (v, id + 1)})) const transform = (ps) => expand ( ps .map (p => ['/', ...p .slice (1) .split ('/')]) .reduce ((a, p) => setPath (p) ({}) (a), {}) ) [0] const paths = ['/Documents/Pictures/Test', '/Documents/Pictures/Test2', '/System', '/Libraries/Node'] console .log (transform (paths)) .as-console-wrapper {max-height: 100% !important; top: 0} La función de utilidad genérica setPath acepta una ruta como ['foo', 'bar', 'baz'] , luego un valor como 42 y finalmente un objeto como {foo : {qux: 100, corge: 200}) , y devuelve un objeto como {foo : {qux: 100, corge: 200, bar: {baz: 42}) . Si faltan rutas intermedias, las crea: matrices si el nodo de la ruta es un número entero, objetos en caso contrario.
La función principal es transform , que
toma su conjunto de entradas, como '/Documents/Pictures/Test' , y convierte cada una en algo como ['/', 'Documents', 'Pictures', 'Test'] Agregamos intencionalmente un extra / al comienzo de cada resultado para dar cuenta del nodo raíz eventual ni claramente presente en el original.
dobla setPath sobre estos resultados, pasando un objeto vacío por cada valor, devolviendo algo como {"/": {Documents: {Pictures: {Test: {}, Test2: {}}}, System: {}, Libraries: {Node: {}}}}
Llama al ayudante de expand , que (recursivamente) convierte eso (casi) en la estructura que está buscando, simplemente envuelta en una matriz, ya que la recursión debe funcionar en una matriz de elementos secundarios.
Extrae el primer valor de la matriz devuelta para brindarle el resultado final.
Encuentro que este tipo de transformación, como una serie de pasos distintos, es mucho más fácil de pensar, y como cada uno es bastante simple, todo puede volverse más fácil de escribir.
Algo como esto:
function getMyData(list) { const data = { id: "0", name: "/", child: [] }; for (let a = 0; a < list.length; a++) { const pathArray = list[a].split("/").splice(1); if (pathArray.length) { for (let i = 0; i < pathArray.length; i++) { const subChild = { id: (i + 1).toString(), name: pathArray[i], child: [], }; data.child.push(subChild); if (pathArray.length > i) { const currentChildIndex = data.child.findIndex( (x) => x.name === pathArray[i] ); const newObj = { id: (i + 2).toString(), name: pathArray[i + 1], child: [], }; if (currentChildIndex === 0 || currentChildIndex) { data.child[currentChildIndex].child.push(newObj); } } pathArray.splice(i); } } } return data; } console.log(getMyData(arr));Pero pensé que podría haber una mejor manera