首页 > 解决方案 > 将带有项目的路径转换为树对象

问题描述

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

从这个数组:

[
  { userName: "1", tags: ["A;B"] },
  { userName: "2", tags: ["A;B"] },
  { userName: "3", tags: ["A;"] },
  { userName: "4", tags: ["A;B;C"] },
  { userName: "5", tags: ["A;B"] },
  { userName: "6", tags: ["A;B;C;D"] } 
]

到这个结构:

[{
    name: "A",
    families: [{
        name: "B",
        families: [{
            name: "C",
            families: [{
                name: "D",
                families: [],
                items: ["6"]
            }],
            items: ["4"]
        }],
        items: ["1", "2", "5"]
    }],
    items: ["3"]
}]
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": []
  }
]

有人可以帮我解决这个问题吗

标签: javascriptnode.jsjsonramda.js

解决方案


请允许我做两个小改动,ramdamergeDeepWithKey将为您完成大部分工作。


更改,在我们开始之前:

  • 制作tags一个数组而不是一个包含一个字符串的数组(即tags[0].split(";")
  • 允许家庭成为类似字典的对象而不是数组(如果您需要数组格式,它是Object.values(dict)

解决方案:

  1. 使用将每个条目转换为所需格式的路径reduce
  2. 使用自定义逻辑合并所有路径:
    • 合并name条目时,不要更改name
    • 合并items条目时,连接

const inp = [
  { userName: "1", tags: ["A","B"] },
  { userName: "2", tags: ["A","B"] },
  { userName: "3", tags: ["A"] },
  { userName: "4", tags: ["A","B","C"] },
  { userName: "5", tags: ["A","B"] },
  { userName: "6", tags: ["A","B","C","D"] } 
];

// Transform an input element to a nested path of the right format
const Path = ({ userName, tags }) => tags
  .slice(0, -1)
  .reduceRight(
    (families, name) => ({ name, families: { [families.name]: families },
      items: []
    }),
    ({ name: last(tags), families: {}, items: [userName] })
  );
    
// When merging path entries, use this custom logic
const mergePathEntry = (k, v1, v2) => 
  k === "name" ? v1 :
  k === "items" ? v1.concat(v2) :
  null;


const result = inp
  .map(Path)
  // Watch out for inp.length < 2
  .reduce(
    mergeDeepWithKey(mergePathEntry)
  )

console.log(JSON.stringify(result, null, 2));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const { mergeDeepWithKey, last } = R;</script>


推荐阅读