skip to Main Content

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!

4

Answers


  1. 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.

    Login or Signup to reply.
  2. You could build the catalog by preserving all categoryId which points to items and later push the products to the category.

    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; }
    Login or Signup to reply.
  3. If product numbers can be longer than category numbers, then maybe also look up the category iteratively:

    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);
    Login or Signup to reply.
  4. You could do it something like this:

    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);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search