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;
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;
Share
Improve this question
edited Apr 12, 2022 at 22:28
Inigo
15.1k5 gold badges50 silver badges81 bronze badges
asked Jan 17, 2016 at 9:07
JamgreenJamgreen
11.1k32 gold badges122 silver badges231 bronze badges
2
- The first is slower for sure. Also you could use a for-loop to make the second faster – CoderPi Commented Jan 17, 2016 at 9:19
-
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 putations (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 putations and thus the two solutions are pletely different. – keenthinker Commented Jan 17, 2016 at 10:16
3 Answers
Reset to default 7For 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 remend 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;
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 if
s 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
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 puting 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!!