如何从具有类别和subCategrie属性的对象的平面数组构建树数组

时间:2018-10-23 14:38:46

标签: javascript arrays data-structures

我正在尝试从平面数组构建树数组,平面数组中的每个项目都需要使用两个属性来构建树数组,它们是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);
}

2 个答案:

答案 0 :(得分:1)

这里成功的关键是创建一个 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);

答案 1 :(得分: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});
    });
  };