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