将字符串列表转换为 recursive/nested dict/json 对象(文件资源管理器类型系统)

convert list of strings to recursive/nested dict/json object (file explorer type system)

我有一个 Redux 商店,它目前正在向我返回一个字符串列表,这些字符串代表下面的文件夹示例

/Documents/Pictures/Test

/Documents/Pictures/Test2

/System

/Libraries/Node

我想将其覆盖到 javascript 中的 dict/json 对象,这样它看起来像:

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 forth

我在这上面花了两天时间,我也尝试过使用 Object.keys 但似乎无法让它工作和匹配但似乎无法让它工作。

这是我当前“有效”但不能递归执行的代码

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

id 并不重要,只要它是唯一的,我知道如何在 python 但不是 javascript 中做到这一点。任何帮助表示赞赏!我觉得我把这个复杂化了......

下面是操作方法。我用代码注释解释

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

递归打印输出

/ 0
Documents 1
Pictures 2
Test 3
Test2 3
System 1
Libraries 1
Node 2

有些像这样:


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

但我认为可能有更好的方法

这是一种基于我used elsewhere:

的相当通用的函数的方法

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}

通用效用函数 setPath 接受诸如 ['foo', 'bar', 'baz'] 的路径,然后接受诸如 42 的值,最后接受诸如 {foo : {qux: 100, corge: 200}) 的 object ,以及 returns 和 object 类似 {foo : {qux: 100, corge: 200, bar: {baz: 42})。如果缺少中间路径,它会创建它们:如果路径节点是整数,则创建数组,否则 objects。

主要功能是transform,其中

  1. 获取您的条目数组,例如 '/Documents/Pictures/Test',并将每个条目变成类似于 ['/', 'Documents', 'Pictures', 'Test'] 我们故意在每个结果的开头添加一个额外的 /考虑到最终的根节点,也没有清楚地出现在原始节点中。

  2. 在这些结果上折叠 setPath,为每个值传递一个空的 object,返回类似 {"/": {Documents: {Pictures: {Test: {}, Test2: {}}}, System: {}, Libraries: {Node: {}}}}

    的内容
  3. 调用 expand 助手,它(递归地)将其(几乎)转换为您正在寻找的结构,只是包装在一个数组中,因为递归需要处理children.

    的数组
  4. 从返回的数组中提取第一个值以提供最终结果。

我发现这种转变,作为一系列不同的步骤更容易思考,而且每个步骤都相当简单,整个事情可以变得更容易编写。