Javascript 将包含项的路径转换为树对象

Javascript 将包含项的路径转换为树对象,javascript,node.js,json,ramda.js,Javascript,Node.js,Json,Ramda.js,我试图将一个包含路径的对象数组转换为数据树,因此我在路径上编写了一个函数路径循环: 从该阵列: [ {用户名:“1”,标签:[“A;B”]}, {用户名:“2”,标签:[“A;B”]}, {用户名:“3”,标记:[“A;”]}, {用户名:“4”,标签:[“A;B;C”]}, {用户名:“5”,标签:[“A;B”]}, {用户名:“6”,标签:[“A;B;C;D”] ] 对于该结构: [{ 名称:“A”, 家庭:[{ 名称:“B”, 家庭:[{ 名称:“C”, 家庭:[{ 名称:“D”, 家庭

我试图将一个包含路径的对象数组转换为数据树,因此我在路径上编写了一个函数路径循环:

从该阵列:

[
{用户名:“1”,标签:[“A;B”]},
{用户名:“2”,标签:[“A;B”]},
{用户名:“3”,标记:[“A;”]},
{用户名:“4”,标签:[“A;B;C”]},
{用户名:“5”,标签:[“A;B”]},
{用户名:“6”,标签:[“A;B;C;D”]
]
对于该结构:

[{
名称:“A”,
家庭:[{
名称:“B”,
家庭:[{
名称:“C”,
家庭:[{
名称:“D”,
家庭:[],
项目:[“6”]
}],
项目:[“4”]
}],
项目:[“1”、“2”、“5”]
}],
项目:[“3”]
}]

有人能帮我解决这个问题吗?

您应该能够使用递归来实现这一点,使用在每个级别调用的getFamilies和getUsers函数:

const allTags=[“A”、“B”、“C”、“D”];
设a=[{“用户名”:“1”,“标记”:[“a;B”]},{“用户名”:“2”,“标记”:[“a;B”]},{“用户名”:“3”,“标记”:[“a;”]},{“用户名”:“4”,“标记”:[“a;B;C”]},{“用户名”:“5”,“标记”:[“a;B”]},{“用户名”:“6”,“标记”:[“a;B;C;D”};
//此函数假定顺序不重要,如果重要,请删除sort()调用。
函数数组相等(a1,a2){
返回a1.length==a2.length&&a1.sort().every(函数(值,索引){返回值===a2.sort()[index]});
}
函数getUserName(标记、arr){
返回arr.filter(v=>arraysEqual(v.tags[0].split(“;”).filter(a=>a),tags)).map({userName}=>userName);
}
函数getFamilies(标记){
如果(tags.length>=allTags.length)返回[];
const name=allTags[tags.length];
常量路径=[…标记,名称];
return[{name,families:getFamilies(path),items:getUserNames(path,a)}];
}
设res=getFamilies([]);

log('Result:',JSON.stringify(res,null,4))
这里的想法是迭代数据(reduce循环),每当映射中缺少节点时(
nodesMap
),使用
createBranch
递归创建节点,创建父节点(如果需要…),然后将节点分配给父节点,依此类推。最后一步是获取根路径的唯一列表(数据中的
a
),并将它们从映射(
)提取到数组中

const createBranch=([name,…tagsList],nodesMap,node)=>{
如果(!nodesMap.has(name)){//如果不在映射中,则创建节点
const node={name,families:[],items:[]};
nodesMap.set(名称、节点);
//如果不是分支的根,请创建父级。。。
if(tagsList.length)createBranch(tagsList、nodesMap、node);
};
//如果父对象将子对象指定给父对象的族
if(node)nodesMap.get(name).families.push(node);
};
const createTree=数据=>{
const tree=data.reduce((nodesMap,{userName:item,tags:[tags]})=>{
const tagsList=tags.match(//[^;]+/g).reverse();//获取分支中的所有节点并反转
const name=tagsList[0];//获取叶
如果(!nodesMap.has(name))createBranch(tagsList,nodesMap);//如果叶不存在,则创建整个分支
nodesMap.get(name).items.push(item);//将项分配给叶的项
返回节点映射;
},新地图());
//获取uniqnue根的列表
常量根=[…新集合(data.map({tags:[tags]})=>tags.split(';')[0]);
返回root.map(root=>tree.get(root));//获取根节点数组
}
const data=[{“用户名”:“1”,“标记”:[“A;B”]},{“用户名”:“2”,“标记”:[“A;B”]},{“用户名”:“3”,“标记”:[“A;”]},{“用户名”:“4”,“标记”:[“A;B;C”]},{“用户名”:“5”,“标记”:[“A;B”]},{“用户名”:“6”,“标记”:[“A;B;C;D”};
const result=createTree(数据);
控制台日志(结果)

.as控制台包装{max height:100%!important;top:0;}
请允许我做两个小改动,ramda的
mergeDeepWithKey
将为您完成大部分工作


更改,在开始之前:

  • 标记设置为数组,而不是包含一个字符串的数组(即
    标记[0]。拆分(“;”)
  • 允许族成为类似字典的对象,而不是数组(如果需要数组格式,则为
    object.values(dict)

解决方案:

  • 使用
    reduce
  • 使用自定义逻辑合并所有路径:
    • 合并
      名称
      条目时,不要更改
      名称
    • 合并
      项时
      项,连接
  • const inp=[
    {用户名:“1”,标签:[“A”,“B”]},
    {用户名:“2”,标签:[“A”,“B”]},
    {用户名:“3”,标签:[“A”]},
    {用户名:“4”,标签:[“A”、“B”、“C”]},
    {用户名:“5”,标签:[“A”,“B”]},
    {用户名:“6”,标签:[“A”、“B”、“C”、“D”]}
    ];
    //将输入元素转换为正确格式的嵌套路径
    常量路径=({userName,tags})=>tags
    .slice(0,-1)
    1.还原右(
    (families,name)=>({name,families:{[families.name]:families},
    项目:[]
    }),
    ({name:last(标记),族:{},项:[userName]})
    );
    //合并路径条目时,请使用此自定义逻辑
    const mergePathEntry=(k,v1,v2)=>
    k==“姓名”?v1:
    k==“项目”?v1.concat(v2):
    无效的
    常量结果=inp
    .map(路径)
    //注意inp.length<2
    .减少(
    mergeDeepWithKey(mergePathEntry)
    )
    log(JSON.stringify(result,null,2))
    
    
    const{mergeDeepWithKey,last}=R
    
    function convertListToTree(associationList) {
        let tree = [];
        for (let i = 0; i < associationList.length; i++) {
            let path = associationList[i].tags[0].split(';');
            let assetName = associationList[i].userName;
            let currentLevel = tree;
            for (let j = 0; j < path.length; j++) {
                let familyName = path[j];
                let existingPath = findWhere(currentLevel, 'name', familyName);
                if (existingPath) {
                    if (j === path.length - 1) {
                        existingPath.items.push(assetName);
                    }
                    currentLevel = existingPath.families;
                } else {
                    let assets = [];
                    if (j === path.length - 1) {
                        assets.push(assetName)
                    }
                    let newPart = {
                        name: familyName,
                        families: [],
                        items: assets,
                    };
                    currentLevel.push(newPart);
                    currentLevel = newPart.families;
                }
            }
        }
        return tree;
    }
    
    function findWhere(array, key, value) {
        let t = 0;
        while (t < array.length && array[t][key] !== value) {
            t++;
        }
        if (t < array.length) {
            return array[t]
        } else {
            return false;
        }
    }
    
    [
      {
        "name": "A",
        "families": [
          {
            "name": "B",
            "families": [
              {
                "name": "C",
                "families": [
                  {
                    "name": "D",
                    "families": [],
                    "items": [
                      "6"
                    ]
                  }
                ],
                "items": [
                  "4"
                ]
              }
            ],
            "items": [
              "1",
              "2",
              "5"
            ]
          },
          {
            "name": "",
            "families": [],
            "items": [
              "3"
            ]
          }
        ],
        "items": []
      }
    ]