在Javascript中组为嵌套的对象数组



我有一个类别数组:

[
{ categoryId: '01', categoryName: 'Byggmaterial' },
{ categoryId: '010', categoryName: 'Bindemedel och bruk' },
{ categoryId: '01001', categoryName: 'Cement' },
{ categoryId: '01002', categoryName: 'Bruksbindemedel' },
{ categoryId: '01003', categoryName: 'Kalkvaror' },
{ categoryId: '011', categoryName: 'Byggnadsblock och ballast' },
{ categoryId: '01101', categoryName: 'Betongblock' },
{ categoryId: '01102', categoryName: 'Tegel' },
{ categoryId: '02', categoryName: 'Byggmaterial' },
{ categoryId: '020', categoryName: 'Bindemedel och bruk' },
{ categoryId: '02001', categoryName: 'Cement' },
{ categoryId: '02002', categoryName: 'Bruksbindemedel' }
......
]

和产品数组:

[
{
productNumber: '01405',
productName: 'SERVALAC AQUA BLANK - Utg�tt'
},
{
productNumber: '01405',
productName: 'SERVALAC AQUA HALVBLANK - Utg�tt'
},
{
productNumber: '03405',
productName: 'SERVALAC AQUA HALVBLANK - Utg�tt'
},
{ productNumber: '03404', productName: 'SCOTTE GT-20 - UTG�TT' },
{ productNumber: '03404', productName: 'SCOTTE 7 - UTG�TT' },
{ productNumber: '03404', productName: 'SCOTTE 7 - UTG�TT' },
{ productNumber: '03404', productName: 'SCOTTE 7 - UTG�TT' },
{ productNumber: '03404', productName: 'SCOTTE 5 - UTG�TT' },
{ productNumber: '03404', productName: 'SCOTTE 20 - UTG�TT' },
......
]

我想把每个产品归入相应的类别。类别也应该像这样分组:主要类别(所有类别都有2位数),第二类(有3位数)和下一类(有5位数)。每种产品应添加到相应的主类、第二类和第三类。

结果应该是这样的:

{
"id":"01",
"categoryName":"Byggmaterial",
"items":[
{
"id":"010",
"categoryName":"Bindemedel och bruk",
"items": [
{
"id":"01001",
"categoryName":"Cement",
"products": [
{
"productNumber":"01001",
"productName":"Tunnfog och Tunnputsbruk A"
},
{
"productNumber":"01001",
"productName":"Tunnfog"
},
.......
]       
}
]       
},
我被卡住了,所以我需要帮助。我试过用"for loop"在"for循环"中,但我只成功地将所有产品分组在主类别中。
{"id":"01","categoryName":"Byggmaterial","items":[{"productNumber":"01001","productName":"Tunnfog och Tunnputsbruk A"},{"productNumber":"01001","productName":"Lagningsmassa fin"},{"productNumber":"01399","productName":"Golvfoam Premium MFR"}, {"productNumber":"01199","productName":"THERMOMUR 350 STD SLUTET"}, {"productNumber":"01701","productName":"Adva Flow 484"},{"productNumber":"01706","productName":"Pieri Decobio C-23"}

除了"for loop"还有其他的解决方案吗?我认为仅仅使用for循环是无法得到想要的结果的。提前感谢!

您可以使用两个相互递归的帮助函数:一个用于为给定的类别选择子类别:

function items(cid, level) {
return categories
.filter(c => c.categoryId.startsWith(cid) && c.categoryId.length === level)
.map(process)
}

和一个处理类别的:

function process(cat) {
let cid = cat?.categoryId || ''
switch (cid.length) {
case 0:
return {...cat, items: items(cid, 2)}
case 2:
return {...cat, items: items(cid, 3)}
case 3:
return {...cat, items: items(cid, 5)}
case 5:
return {
...cat,
products: products.filter(p => p.productNumber.startsWith(cid))
}
}
}

最后,只调用不带参数的process()

您可以通过保留所有指向itemscategoryId来构建目录,然后将产品推入该类别。

const
categories = [{ categoryId: '01', categoryName: 'Byggmaterial' }, { categoryId: '010', categoryName: 'Bindemedel och bruk' }, { categoryId: '01001', categoryName: 'Cement' }, { categoryId: '01002', categoryName: 'Bruksbindemedel' }, { categoryId: '01003', categoryName: 'Kalkvaror' }, { categoryId: '011', categoryName: 'Byggnadsblock och ballast' }, { categoryId: '01101', categoryName: 'Betongblock' }, { categoryId: '01102', categoryName: 'Tegel' }, { categoryId: '02', categoryName: 'Byggmaterial' }, { categoryId: '020', categoryName: 'Bindemedel och bruk' }, { categoryId: '02001', categoryName: 'Cement' }, { categoryId: '02002', categoryName: 'Bruksbindemedel' }],
products = [{ productNumber: "01001", productName: "Tunnfog och Tunnputsbruk A" }, { productNumber: "01001", productName: "Tunnfog" }],
catalog = categories.reduce((r, { categoryId, categoryName }) => {
let i = categoryId.length;
do {
const key = categoryId.slice(0, i);
if (key in r) {
(r[key].items ??= []).push(r[categoryId] = { categoryId, categoryName });
return r;
}
} while (--i)
r.items.push(r[categoryId] = { categoryId, categoryName });
return r;
}, { items: [] });
products.forEach(o => (catalog[o.productNumber].product ??= []).push(o));
console.log(catalog.items);
.as-console-wrapper { max-height: 100% !important; top: 0; }

如果产品编号可以比类别编号长,那么也可以迭代查找类别:

function hierarchy(categories, products) {
function parent(s) {
while (!map.has(s)) s = s.slice(0, -1);
return map.get(s);
}
const root = { items: [] };
const map = new Map(categories.map(({categoryId: id, ...rest}) => [id, { id, ...rest }])).set("", root);
for (const cat of categories) {
(parent(cat.categoryId.slice(0, -1)).items ??= []).push(map.get(cat.categoryId));
}
for (const prod of products) {
(parent(prod.productNumber).products ??= []).push(prod);
}
return root.items;
}
// Demo
const categories = [
{ categoryId: '01', categoryName: 'Byggmaterial' },
{ categoryId: '010', categoryName: 'Bindemedel och bruk' },
{ categoryId: '01001', categoryName: 'Cement' },
{ categoryId: '01002', categoryName: 'Bruksbindemedel' },
{ categoryId: '01003', categoryName: 'Kalkvaror' },
{ categoryId: '011', categoryName: 'Byggnadsblock och ballast' },
{ categoryId: '01101', categoryName: 'Betongblock' },
{ categoryId: '01102', categoryName: 'Tegel' },
{ categoryId: '02', categoryName: 'Byggmaterial' },
{ categoryId: '020', categoryName: 'Bindemedel och bruk' },
{ categoryId: '02001', categoryName: 'Cement' },
{ categoryId: '02002', categoryName: 'Bruksbindemedel' }
];
const products  = [
{ productNumber: '01001', productName: 'Tunnfog och Tunnputsbruk A' },
{ productNumber: '01001',  productName: 'Tunnfog' },
];
const forest = hierarchy(categories, products);
console.log(forest);

你可以这样做:

const getNextPrefixLength = (prefix) =>
// Needed because level 3 has 5 chars
prefix.length + (prefix.length === 3 ? 2 : 1);
const getCategoriesByPrefix = (idPrefix) =>
categories.filter(
({ categoryId }) =>
categoryId.length === getNextPrefixLength(idPrefix) &&
categoryId.startsWith(idPrefix)
);
const getGroupedCategoryRecursively = ({ categoryId: id, categoryName }) => ({
id,
categoryName,
items: getCategoriesByPrefix(id).map(getGroupedCategoryRecursively),
});
const result = getCategoriesByPrefix("0").map(getGroupedCategoryRecursively);