map(), reduce() and filter vs forEach()
Asked Answered
P

3

6

I have just learned about MapReduce, so I wondered if there are any advantages in writing

const initialValue = 0;

if (this.items) {
  return this.items.filter(function (item) {
    return item && item.quantity && item.price;
  }).reduce(function(previousValue, currentValue) {
    return previousValue + currentValue.quantity * currentValue.price ;
  }, initialValue);
} else {
  return initialValue;
}

instead of just

let total = 0;
if (this.items) {
  this.items.forEach(function(item) {
    if (item && item.quantity && item.price) {
      total += item.quantity * item.price;
    }
  });
}
return total;
Plummer answered 17/1, 2016 at 9:7 Comment(2)
The first is slower for sure. Also you could use a for-loop to make the second fasterRufford
If you use MapReduce just to accumulate values (in an iterative way), then there is really no advantage of the first over the second. However don't forget, that the idea behind MapReduce is to be used for parallel computations (distributed systems / clusters). So if the forEach isn't designed resp. implemented to be run in parallel, then it is not suited for distributed environments and the second solution will not work for parallel computations and thus the two solutions are completely different.Babyblueeyes
B
7

For future readers, there are a few more idiomatic ways to write the reduction in a functional way. These are generally used because they convey intent a bit more cleanly (and don't add a variable to the scope).

Note: I am assuming this.items has type

({ quantity: number; price: number } | undefined)[] | undefined

but each of the examples is tolerant to even more invalid data than the two in the question.

Filtering and mapping before reducing

Default value at the end

return this.items
    ?.filter(item => item?.quantity && item.price)
    .map(item => item.quantity * item.price)
    .reduce((a, b) => a + b, 0) ?? 0

Default array at the start

return (this.items ?? [])
    .filter(item => item?.quantity && item.price)
    .map(item => item.quantity * item.price)
    .reduce((a, b) => a + b, 0)

Handling the filter within the map

I would not recommend these just because the previous two convey intention more clearly.

Default value at the end

return this.items
    ?.map(item => (item?.quantity ?? 0) * (item?.price ?? 0))
    .reduce((a, b) => a + b, 0) ?? 0

Default array at the start

return (this.items ?? [])
    .map(item => (item?.quantity ?? 0) * (item?.price ?? 0))
    .reduce((a, b) => a + b, 0)

Destructuring

Each of the previous examples can be done with destructuring instead. I am including one example.

return (this.items ?? [])
    .filter(item => item) // Ensure item exists; sufficient for the cases we need to worry about
    .map(({ price = 0, quantity = 0 }) => quantity * price)
    .reduce((a, b) => a + b, 0)

Without a map

We can now do the reduction without a map. This can also be done without destructuring, but that is seemingly (to me) inelegant.

return (this.items ?? [])
    .filter(item => item)
    .reduce((sum, { price = 0, quantity = 0 }) => sum + quantity * price, 0)

Of course, you can change the filter condition, which takes us back to roughly the first example in the question:

return (this.items ?? [])
    .filter(item => item?.price && item.quantity)
    .reduce((sum, { price, quantity }) => sum + quantity * price, 0)

Original forEach loop

Some of these changes can be made to the original loop, too:

let total = 0;
items?.forEach((item) => {
  if (item?.quantity && item.price) {
    total += item.quantity * item.price;
  }
});
return total;
Binucleate answered 21/2, 2021 at 5:36 Comment(0)
R
0

I can't see any advantage of the first over the second*. However the second is even faster then the first and looks more clean! The purpose of the first might be to demonstrate the use of built-in array-functions.

However mapreduce is used for a lot of Elements, so you might the speed it up as much as you can. This should be the fastest you can get:

const initialValue = 0;
let total = initialValue;
if (this.items) {
  for (var i = this.items.length; i--;) {
    let item = this.items[i]
    if (item && item.quantity && item.price) {
      total += item.quantity * item.price;
    }
  }
  return total;
} else {
  return initialValue 
}

In addtion you could drop the if inside the loop, if you know that your array is consitant. Both ifs are just there to make sure the array is properly build and the script doesn't run into an Error, that would be usefull for userdata input, but in a closed system you don't need them.


*I noticed that, the second is missing the default value return initialValue

Rufford answered 17/1, 2016 at 9:54 Comment(0)
E
-1

Let's first understand forEach by using the code given below:

        let arr = [2,3,4,5,6];
        
        arr.forEach((value,index,array) => {
            console.log(value,index,array);
        })

In the above code we has used forEach loop in which we can give parameters like value, index, array -- This will return the value then index of that value and full array.

Lets understand maps in maps it will create a new array and return that array

        let a = arr.map((value , index ,array)=>{
            console.log(value , index,array);
            return value + 1;
        })
        console.log(a);
In this you can return the values or change as per you preference like return value*10 or return value + 1;

Lets understand filter in filter it will create a new array and return that array

        let arr2 = [42,3,1,45,5,55];
        
        let a2 = arr2.filter((value)=>{
        return value < 10;
        })
    
    console.log(a2);
In this we can give some condition like return value>10 or return value < 10 ans many more

In reduce method it will return a single value by computing the function

        let arr3 = [1,2,3,5,2,1];
         
        let newarr3 = arr3.reduce((val1,val2) => {
        return val1+val2;
        })
        console.log(newarr3);

In above code firstly val1=1,val2=2 then they sum up as 3 and not val1=3 and val2= is 5 so it will sum up all the elements in an array and return an ans as 14.

IMPORTANT - MAP,FILTER,REDUCE doesn't change your existing array!!

Eden answered 3/8, 2023 at 20:54 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.