1

I've been having this small problem that has been tried to sweepen under the carpet.

I am calling a API which are product, each products has a key categories with a value of a array of maps. Trying to categorize these per category.

This is my example response from the API

[
  {
    "name": "Product one",
    "categories": [
      {
        "id": 1,
        "name": "Category One"
      }
    ]
  },
  {
    "name": "Product Two",
    "categories": [
      {
        "id": 1,
        "name": "Category One"
      },
      {
        "id": 2,
        "name": "Category two"
      }
    ]
  }
]

The code I've composed follows below:

 function groupBy(arr) {
      let categories = [];
      arr.forEach(el => {
        el.categories.forEach(c => {
          // Skapa ny kategori om inte redan existerar
          if (!categories.includes(c.id)) {
            categories.push({
              name: c.name,
              id: c.id,
              products: [el]
            });
          } else {
            // Lägg till produkt i existerande kategori
            categories.forEach(_c => {
              if (_c.id === c.id) {
                _c.products.push(el)
              }
            })
          }
        });
      });

      return categories;
    }

groupBy(arr);

I think I am overdoing it somehow, the desired result is of course without any duplicate categories, products should be pushed into the products[].

Screenshot of console output in Chrome browser

2
  • what is the expected json result ? Commented Apr 4, 2020 at 20:02
  • you want to group products based on category ? Commented Apr 4, 2020 at 20:03

2 Answers 2

2

You can do this by first iterating over the products to assign it to each of the categories. To prevent duplication you want to use a map or set; here I use a javascript object as a map. Once you have that, you can convert it to an array like you want by using Object.values() on the map object

const groupProductsByCategory = (products) => {
  // reduce down each product into a category map
  const productsGroupedByCategory = products.reduce((categories, product) => {
    // insert the current product into each of the categories it contains
    product.categories.forEach(category => {
      // if the category exists in the map, we just need to append the current product
      if (category.id in categories)
        categories[category.id].products.push(product)
      else // otherwise, create a new category object with the current product
      categories[category.id] = ({ ...category, products: [product] })
    })
    return categories
  }, ({}))

  // convert the object into an array of objects
  return Object.values(productsGroupedByCategory)
}
Sign up to request clarification or add additional context in comments.

1 Comment

Very documented answer and a quick one! Muchos gracias @RobBrander
2

Using map-reduce can be done.

const data = [{"name":"Product one","categories":[{"id":1,"name":"Category One"}]},{"name":"Product Two","categories":[{"id":1,"name":"Category One"},{"id":2,"name":"Category two"}]}];

function categoriesList(list = []) {
  return Object.values(
    list.reduce((arr, product) => {
      product.categories.forEach((cat) => {
        if (!arr[cat.id]) arr[cat.id] = { ...cat, products: [] };
        arr[cat.id].products.push(product);
      });
      return arr;
    }, {})
  );
}
console.log(JSON.stringify(categoriesList(data), null, 4));
.as-console {
  min-height: 100% !important;
}

.as-console-row {
  color: blue !important;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.