在JavaScript中将分组为嵌套对象数组。

huangapple go评论67阅读模式
英文:

Group into nested array of objects in Javascript

问题

{
"我有一个类别数组:[\n { categoryId: '01', categoryName: 'Byggmaterial' },\n { categoryId: '010', categoryName: 'Bindemedel och bruk' },\n { categoryId: '01001', categoryName: 'Cement' },\n { categoryId: '01002', categoryName: 'Bruksbindemedel' },\n { categoryId: '01003', categoryName: 'Kalkvaror' },\n { categoryId: '011', categoryName: 'Byggnadsblock och ballast' },\n { categoryId: '01101', categoryName: 'Betongblock' },\n { categoryId: '01102', categoryName: 'Tegel' },\n { categoryId: '02', categoryName: 'Byggmaterial' },\n { categoryId: '020', categoryName: 'Bindemedel och bruk' },\n { categoryId: '02001', categoryName: 'Cement' },\n { categoryId: '02002', categoryName: 'Bruksbindemedel' }\n......\n]\n\n和一个产品数组:[\n {\n productNumber: '01405',\n productName: 'SERVALAC AQUA BLANK - Utgått'\n },\n {\n productNumber: '01405',\n productName: 'SERVALAC AQUA HALVBLANK - Utgått'\n },\n {\n productNumber: '03405',\n productName: 'SERVALAC AQUA HALVBLANK - Utgått'\n },\n { productNumber: '03404', productName: 'SCOTTE GT-20 - UTGÅTT' },\n { productNumber: '03404', productName: 'SCOTTE 7 - UTGÅTT' },\n { productNumber: '03404', productName: 'SCOTTE 7 - UTGÅTT' },\n { productNumber: '03404', productName: 'SCOTTE 7 - UTGÅTT' },\n { productNumber: '03404', productName: 'SCOTTE 5 - UTGÅTT' },\n { productNumber: '03404', productName: 'SCOTTE 20 - UTGÅTT' }\n......\n]\n\n我想将每个产品分组到相应的类别中。\n类别也应该像这样分组:主类别(所有两位数的类别),第二类别(三位数的类别)和下一个类别(五位数的类别)。\n每个产品应该添加到相应的主类别、第二类别和第三类别。\n\n结果应该如下所示:\n\n{\n "id":"01",\n "categoryName":"Byggmaterial",\n "items":[\n {\n "id":"010",\n "categoryName":"Bindemedel och bruk",\n "items": [\n {\n "id":"01001",\n "categoryName":"Cement",\n "products": [\n {\n "productNumber":"01001",\n "productName":"Tunnfog och Tunnputsbruk A"\n },\n {\n "productNumber":"01001",\n "productName":"Tunnfog"\n },\n .......\n ]\n }\n ]\n }\n ]\n}\n\n\n我被卡住了,所以我需要帮助。\n我尝试使用“for循环”嵌套“for循环”,但我只成功地将所有产品分组到主类别中。\n\n{...}
}

英文:

I have an array of 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' }
......
]

And an array of products:

[
  {
    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' },
......
]

I want to group each product in the corresponding category.
The categories should be also grouped like this: main category (all the categories with 2 digits), second category (with 3 digits) and the next one (with 5 digits).
Each product should be added to the corresponding main category, second and third category.

The result should be like this:

{
    "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"
                          },
                          .......
                    ]       
        }
                    ]       
        },

I'm stuck so I need help.
I've tried with "for loop" inside "for loop", but I only succeeded to group all the products in main category.

{"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"}

Is there any other solution then "for loop"? I think that I cannot get the wanted result with only for loop.
Thank you in advance!

答案1

得分: 1

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

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()

英文:

You can use two mutually recursive helper functions: one that selects subcategories for the given category:

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

and one that processes a category:

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))
            }
    }
}

Finally, just invoke process() with no arguments.

答案2

得分: 1

你可以通过保留指向"items"的所有"categoryId"来构建目录,然后将产品添加到相应的类别中。

英文:

You could build the catalog by preserving all categoryId which points to items and later push the products to the category.

<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
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 =&gt; (catalog[o.productNumber].product ??= []).push(o));

console.log(catalog.items);

<!-- language: lang-css -->
.as-console-wrapper { max-height: 100% !important; top: 0; }
<!-- end snippet -->

答案3

得分: 0

Here is the translated code snippet without the code comments:

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: 'Building Materials' },
  { categoryId: '010', categoryName: 'Binders and Mortars' },
  { categoryId: '01001', categoryName: 'Cement' },
  { categoryId: '01002', categoryName: 'Mortars' },
  { categoryId: '01003', categoryName: 'Lime Products' },
  { categoryId: '011', categoryName: 'Building Blocks and Aggregate' },
  { categoryId: '01101', categoryName: 'Concrete Blocks' },
  { categoryId: '01102', categoryName: 'Brick' },
  { categoryId: '02', categoryName: 'Building Materials' },
  { categoryId: '020', categoryName: 'Binders and Mortars' },
  { categoryId: '02001', categoryName: 'Cement' },
  { categoryId: '02002', categoryName: 'Mortars' }
];

const products  = [
  { productNumber: '01001', productName: 'Thin Joints and Thin Plaster Mortar A' },
  { productNumber: '01001', productName: 'Thin Joints' },
];

const forest = hierarchy(categories, products);
console.log(forest);

This code snippet has been translated.

英文:

If product numbers can be longer than category numbers, then maybe also look up the category iteratively:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

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}) =&gt; [id, { id, ...rest }])).set(&quot;&quot;, 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: &#39;01&#39;, categoryName: &#39;Byggmaterial&#39; },
{ categoryId: &#39;010&#39;, categoryName: &#39;Bindemedel och bruk&#39; },
{ categoryId: &#39;01001&#39;, categoryName: &#39;Cement&#39; },
{ categoryId: &#39;01002&#39;, categoryName: &#39;Bruksbindemedel&#39; },
{ categoryId: &#39;01003&#39;, categoryName: &#39;Kalkvaror&#39; },
{ categoryId: &#39;011&#39;, categoryName: &#39;Byggnadsblock och ballast&#39; },
{ categoryId: &#39;01101&#39;, categoryName: &#39;Betongblock&#39; },
{ categoryId: &#39;01102&#39;, categoryName: &#39;Tegel&#39; },
{ categoryId: &#39;02&#39;, categoryName: &#39;Byggmaterial&#39; },
{ categoryId: &#39;020&#39;, categoryName: &#39;Bindemedel och bruk&#39; },
{ categoryId: &#39;02001&#39;, categoryName: &#39;Cement&#39; },
{ categoryId: &#39;02002&#39;, categoryName: &#39;Bruksbindemedel&#39; }
];
const products  = [
{ productNumber: &#39;01001&#39;, productName: &#39;Tunnfog och Tunnputsbruk A&#39; },
{ productNumber: &#39;01001&#39;,  productName: &#39;Tunnfog&#39; },
];
const forest = hierarchy(categories, products);
console.log(forest);

<!-- end snippet -->

答案4

得分: 0

你可以像这样做:

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);
英文:

You could do it something like this:

const getNextPrefixLength = (prefix) =&gt;
  // Needed because level 3 has 5 chars
  prefix.length + (prefix.length === 3 ? 2 : 1);

const getCategoriesByPrefix = (idPrefix) =&gt;
  categories.filter(
    ({ categoryId }) =&gt;
      categoryId.length === getNextPrefixLength(idPrefix) &amp;&amp;
      categoryId.startsWith(idPrefix)
  );

const getGroupedCategoryRecursively = ({ categoryId: id, categoryName }) =&gt; ({
  id,
  categoryName,
  items: getCategoriesByPrefix(id).map(getGroupedCategoryRecursively),
});

const result = getCategoriesByPrefix(&quot;0&quot;).map(getGroupedCategoryRecursively);

huangapple
  • 本文由 发表于 2023年4月6日 20:32:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/75949562.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定