Home > Software engineering >  How to use 'reduce' function to filter and display products in cart
How to use 'reduce' function to filter and display products in cart

Time:04-12

I am trying to train myself with Javascript and am a bit confused with reduce and would appreciate some guidance in understanding it. I've done a lot of research and have arrived at a stage where some clarity beyond just googling is needed. So here's my basic sample code..

the ids are the cart ids and the products are the products in a store

      const ids = [4, 3, 1];
      const products = [
        { id: 1, product: "Product 1" },
        { id: 2, product: "Product 2" },
        { id: 3, product: "Product 3" },
        { id: 4, product: "Product 4" },
        { id: 5, product: "Product 5" },
        { id: 6, product: "Product 6" },
        { id: 7, product: "Product 7" },
      ];


I need to display the products which are in the cart so I went with 3 options The for loop, the map and filter and finally the reduce


FOR LOOP

      // SOLUTION WITH FOR LOOP
      const selectedProducts = [];
      for (id of ids) {
        const selectedProduct = products.filter((product) => product.id === id);
        selectedProducts.push(selectedProduct);
      }
      console.log("selectedProducts", selectedProducts);

Question 1 : Currently I have just 7 products. but in an actual store there would be thousands of products. So is filtering thousands of products for each id a good idea. Is there a better way to do this?


MAP and FILTER

      // SOLUTION WITH MAP
      const mappedProducts = ids.map((id) => {
        const [obj] = products.filter((product) => product.id === id);
        return obj;
      });
      console.log("mappedProducts", mappedProducts);

Question 2 : As filter creates an array I ended up with an array of arrays and had to destructure the array and return the object. Is there a better way where I could directly destructure/pass the object without explicitly declaring return.


REDUCE

      // SOLUTION WITH REDUCE
      const initialArray = [];
      const reducedProducts = products.reduce(function (acc, product) {
        const productId = product.id;
        if (ids.includes(product.id)) acc.push(product);
        return acc;
      }, initialArray);

      console.log("reducedProducts", reducedProducts);
      console.log("initialArray", initialArray);

Question 3 : What am I doing wrong with reduce?

This is my first time with reduce and I am sure I am doing something wrong here.. as reduce is supposed to be more compact than the for and map-filter combination but in my case it seems to be the opposite. Also I thought that with reduce the initialValue does not get mutated. But in my case it is getting mutated.

Any help and advice would be appreciated.

CodePudding user response:

So is filtering thousands of products for each id a good idea. Is there a better way to do this?

From the products array, allow for easy lookup by restructuring it into an object or map indexed by ID, so you just need to use ordinary bracket notation or .get to get the matching object (O(1)).

const ids = [4, 3, 1];
const products = [
  { id: 1, product: "Product 1" },
  { id: 2, product: "Product 2" },
  { id: 3, product: "Product 3" },
  { id: 4, product: "Product 4" },
  { id: 5, product: "Product 5" },
  { id: 6, product: "Product 6" },
  { id: 7, product: "Product 7" },
];
const productsById = Object.fromEntries(products.map(
  obj => [obj.id, obj]
));
const result = ids.map(id => productsById[id]);
console.log(result);

Is there a better way where I could directly destructure/pass the object without explicitly declaring return.

You could .find instead, which returns the matching object instead of returning an array - but that's still an O(n ^ 2) process. Indexing each product by its ID is better.

as reduce is supposed to be more compact than the for and map-filter combination but in my case it seems to be the opposite. Also I thought that with reduce the initialValue does not get mutated. But in my case it is getting mutated.

Not at all - .reduce, when not used in the appropriate circumstance, is indeed more verbose than more standard loops, as you're seeing. See this video.

Also I thought that with reduce the initialValue does not get mutated.

Sure it can, if the initial value is an object (and not a primitive) - objects can be mutated. If you mutate the accumulator parameter and return it, the next iteration (and the next, and the final return value) is the same exact object.

CodePudding user response:

Just for note, a couple of examples with high-order functions:

reduce

products.reduce((acc, product) => {
    if (ids.includes(product.id)) acc.push(product);
    return acc;
}, []);

filter

products.filter((product) => ids.includes(product.id));
// or with destructuring 
products.filter(({ id }) => ids.includes(id));

map and flat

products.map((product) => (ids.includes(product.id)) ? product : []).flat();

flatMap

products.flatMap((product) => (ids.includes(product.id)) ? product : []);
  • Related