【问题标题】:How to build tree array from flat array of object with category and subCategrie properties如何从具有类别和子类别属性的平面对象数组构建树数组
【发布时间】:2018-10-23 14:38:45
【问题描述】:

我正在尝试从平面数组构建树数组,平面数组中的每个项目都有两个属性需要用于构建树数组,它们是 1. 类别。 2. subCategrie 是字符串数组。

let data = [
  {
    id: 1,
    name: "Zend",
    category: "php",
    subCategory: ["framework"]
  },
  {
    id: 2,
    name: "Laravel",
    category: "php",
    subCategory: ["framework"]
  },
  {
    id: 3,
    name: "Vesion 5",
    category: "php",
    subCategory: ["versions"]
  },
  {
    id: 4,
    name: "Angular",
    category: "frontend",
    subCategory: ["framework", "typescript"]
  },
  {
    id: 5,
    name: "Aurelia",
    category: "frontend",
    subCategory: ["framework", "typescript"]
  },
  {
    id: 6,
    name: "JQuery",
    category: "frontend",
    subCategory: []
  }
];

应该是

    let tree = [
      {
        name: "php",
        children: [
          {
            name: "framework",
            children: [
              {
                id: 1,
                name: "Zend"
              },
              {
                id: 2,
                name: "Laravel"
              }
            ]
          },
          {
            name: "versions",
            children: [
              {
                id: 3,
                name: "Vesion 5"
              }
            ]
          }
        ]
      }
 // ...
    ];

有没有解决类似问题的文章、链接? 我尝试了很多次,但在尝试构建子类别时卡住了。

这是我最后一次抛出错误的尝试,我知道这是错误的,但它是为那些想看到我的尝试的人准备的

const list = require('./filter.json')
let tree = {};
for (let filter of list) {
    if (tree[filter.category]) {
        tree[filter.category].push(filter);
    } else {
        tree[filter.category] = [filter];
    }
}
function buildChildren(list, subcategories, category, index) {
    let tree = {}
    for (let filter of list) {
        if (filter.subcategory.length) {
            for (let i = 0; i < filter.subcategory.length; i++) {
                let branch = list.filter(item => item.subcategory[i] === filter.subcategory[i]);
                branch.forEach(item =>{
                    if (tree[filter.subcategory[i]]){
                        tree[filter.subcategory[i]] = tree[filter.subcategory[i]].push(item)
                    }else{
                        tree[item.subcategory[i]] = [item]
                    }
                })
            }
        }
    }

    console.log('tree ', tree);
}

【问题讨论】:

  • 您应该提供您尝试解决整个问题的代码!
  • @SMH 你有任何代码尝试吗?
  • 是的,我确实有代码尝试,这是我最后一次尝试,它会引发错误,而且非常复杂。我会提出这个问题

标签: javascript arrays data-structures


【解决方案1】:

请注意,对于 javascript,我通常使用 Lodash(通常在代码中写为 _),但这些方法中的大多数也应该内置到 javascript 中的对象中(即 _.forEach = Array. forEach())

    const tree = [];
    // First Group all elements of the same category (PHP, Frontend, etc.)
    data = _.groupBy(data, 'category');
    _.forEach(data, function (categoryElements, categoryName) {
      // Each Category will have it's own subCategories that we will want to handle
      let categorySubCategories = {};
      // The categoryElements will be an array of all the objects in a given category (php / frontend / etc..)
      categoryElements.map(function (element) {
        // For each of these categoryies, we will want to grab the subcategories they belong to
        element.subCategory.map(function (subCategoryName) {
          // Check if teh category (PHP) already has already started a group of this subcategory,
          // else initialize it as an empty list
          if (!categorySubCategories[subCategoryName]) { categorySubCategories[subCategoryName] = []; }
          // Push this element into the subcategory list
          categorySubCategories[subCategoryName].push({id: element.id, name: element.name});
        });
      });
      // Create a category map, which will be a list in the format {name, children}, created from
      // our categorySubCategories object, which is in the format {name: children}
      let categoryMap = [];
        _.forEach(categorySubCategories, function (subCategoryElements, subCategoryName) {
          categoryMap.push({name: subCategoryName, children: subCategoryElements});
        });
      // Now that we've grouped the sub categories, just give the tree it's category name and children
      tree.push({name: categoryName, children: categoryMap});
    });
  };

【讨论】:

    【解决方案2】:

    这里成功的关键是创建一个 interim 格式,以便于查找。因为您使用的是children 数组,所以每次添加新内容时都必须使用filterfind,以防止重复并确保分组。

    通过使用基于对象和键的格式,可以更轻松地进行分组。

    我们可以在一个嵌套循环中创建组,这意味着我们在主逻辑中只触及每个项目一次。该组具有以下格式:

    { "categoryName": { "subCategoryName": [ { id, name } ] } }
    

    然后,获得所需的{ name, children } 格式是在这棵树的条目上再循环一次。在这个循环中,我们从{ "categoryName": catData } 移动到{ name: "categoryName", children: catData }

    这是一个分别显示两个步骤的示例:

    const data=[{id:1,name:"Zend",category:"php",subCategory:["framework"]},{id:2,name:"Laravel",category:"php",subCategory:["framework"]},{id:3,name:"Vesion 5",category:"php",subCategory:["versions"]},{id:4,name:"Angular",category:"frontend",subCategory:["framework","typescript"]},{id:5,name:"Aurelia",category:"frontend",subCategory:["framework","typescript"]},{id:6,name:"JQuery",category:"frontend",subCategory:[]}];
    
    // { category: { subCategory: [ items ] } }
    const categoryOverview = data.reduce(
      (acc, { id, name, category, subCategory }) => {
        // Create a top level group if there isn't one yet
        if (!acc[category]) acc[category] = {};
        
        subCategory.forEach(sc => { 
          // Create an array for this subCat if there isn't one yet
          acc[category][sc] = (acc[category][sc] || [])
            // and add the current item to it
            .concat({ id, name }); 
        });
        
        return acc;
      },
      {}
    )
    
    const nameChildrenMap = Object
      .entries(categoryOverview)
      // Create top level { name, children } objects
      .map(([cat, subCats]) => ({
        name: cat,
        children: Object
          .entries(subCats)
          // Create sub level { name, children } objects
          .map(([subCat, items]) => ({
            name: subCat,
            children: items
          }))
      }))
    
    console.log(nameChildrenMap);

    【讨论】:

    • 感谢您的聪明回答,但是我想我无法很好地解释树的结构应该如何。目前,您的解决方案在frontend 类别 1.framekwork 2. typescript 上创建了两个子类别,但我试图让树更深,所以它应该是这样的前端 |--- 框架 |--- 打字稿 |--- Angular |--- Aurelia { 名称:前端,}
    • 啊,你用// ... 把那部分从你想要的结果中删掉了;)我看看我今天晚些时候是否有时间更新
    猜你喜欢
    • 2021-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-25
    相关资源
    最近更新 更多