'Javascript: How would you get the highest rated product category based on these 2 arrays

Assuming I have 2 arrays:

const products = [
  {
    name: 'prod1',
    category: 'Meat'
  },
  {
    name: 'prod2',
    category: 'Meat'
  },
  {
    name: 'prod3',
    category: 'Dairy'
  }];


const rate = [
  {
    name: 'prod1',
    rate: 23,
  },
  {
    name: 'prod2',
    rate: 36
  },
  {
    name: 'prod3',
    rate: 50,
  }];

How would you get the category that has the highest sum rate? For example, prod1 and prod2 share the same category 'Meat" and hence the rate for meat is 36 + 23 = 59.

The way I thought about it is to create an adjusted Array of products where each entry will contain the rate from the second array and then I will create a result array and push an object of category and sumRate after iterating the adjustedArray.

So if the result Array has an object with category, I would adjust the sum and add the new rate, if not I'll create a new entry with category: rate.

Can we do this in a very optimal way?



Solution 1:[1]

As the OP probably knows, canonical grouping goes like this...

const prodsByCategory = products.reduce((acc, p) => {
  let cat = p.category;
  if (!acc[cat]) acc[cat] = [];
  acc[cat].push(p);
  return acc;
}, {});

Modify this a little to add the data which will be needed to optimize.

const prodsByCategory = products.reduce((acc, p) => {
  let cat = p.category;
  // instead of just an array, keep an array and a total
  if (!acc[cat]) acc[cat] = { products: [], totalRate: 0 };
  // instead of just pushing, push and increment total with a lookup
  acc[cat].products.push(p);
  acc[cat].totalRate += rateForProduct(p) || 0;
  return acc;
}, {});

We need a lookup for rateForProduct, like this:

const rateForProduct = product => {
  return rate.find(r => r.name === product.name)?.rate || 0;
}

That should produce an object keyed by category with values that have a prop called totalRate. Sort those entries so that the first one is maximized. Here's a demo...

const products = [{
    name: 'prod1',
    category: 'Meat'
  },
  {
    name: 'prod2',
    category: 'Meat'
  },
  {
    name: 'prod3',
    category: 'Dairy'
  }
];

const rate = [{
    name: 'prod1',
    rate: 23,
  },
  {
    name: 'prod2',
    rate: 36
  },
  {
    name: 'prod3',
    rate: 50,
  }
];

const rateForProduct = product => {
  return rate.find(r => r.name === product.name)?.rate || 0;
}

const prodsByCategory = products.reduce((acc, p) => {
  let cat = p.category;
  if (!acc[cat]) acc[cat] = {
    products: [],
    totalRate: 0
  };
  acc[cat].products.push(p);
  acc[cat].totalRate += rateForProduct(p);
  return acc;
}, {});

const sortedEntries = Object.entries(prodsByCategory).sort((a, b) => b[1].totalRate - a[1].totalRate);

const bestEntry = {
  category: sortedEntries[0][0],
  rate: sortedEntries[0][1].totalRate
}
console.log(bestEntry);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 danh