Как построить массив деревьев из плоского массива объектов со свойствами категорий и подкатегорий

1

Я пытаюсь построить массив деревьев из плоского массива, каждый элемент в плоском массиве имеет два свойства, которые необходимо использовать для построения массива деревьев, это 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);
}
  • 1
    Вы должны предоставить код вашей попытки решить всю проблему!
  • 0
    @SMH SMH у тебя есть какие-либо попытки кода?
Показать ещё 1 комментарий
Теги:
arrays
data-structures

2 ответа

1

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 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 category name and children
      tree.push({name: categoryName, children: categoryMap});
    });
  };
1

Ключом к успеху здесь является создание промежуточного формата, который позволяет легко искать. Поскольку вы работаете с 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);
  • 0
    Спасибо за ваш умный ответ, однако я думаю, что не смог бы очень хорошо объяснить, какой должна быть структура дерева. В настоящее время ваше решение создает две подкатегории в категории внешнего frontend 1.framekwork 2. машинописный текст, но я пытаюсь сделать дерево глубже, поэтому оно должно выглядеть примерно так: внешний интерфейс | --- frameworks | --- typescript | --- Angular | --- Аурелия {имя: фронтенд,}
  • 0
    Ах, вы вырезали эту часть из желаемого результата с помощью // ... ;) Я посмотрю, успею ли я обновить сегодня

Ещё вопросы

Сообщество Overcoder
Наверх
Меню