Как построить массив деревьев из плоского массива объектов со свойствами категорий и подкатегорий - PullRequest
0 голосов
/ 23 октября 2018

Я пытаюсь построить массив деревьев из плоского массива, каждый элемент в плоском массиве имеет два свойства, которые необходимо использовать для построения массива дерева, они 1. категории.2. подкатегория, которая является массивом строк.

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 голосов
/ 23 октября 2018

Heads up, для 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});
    });
  };
0 голосов
/ 23 октября 2018

Ключом к успеху здесь является создание промежуточного формата, который позволяет легко искать.Поскольку вы работаете с массивами children, вам в конечном итоге придется использовать filter и find всякий раз, когда вы добавляете что-то новое, для предотвращения дублирования и обеспечения группировки.

Работая с форматом, основанным на объектах иключи, намного проще выполнить группировку.

Мы можем создавать группы в одном вложенном цикле, что означает, что мы касаемся каждого элемента только один раз для основной логики.Группа имеет следующий формат:

{ "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);
...