Can someone explain how this is working.
Learning to use Array.reduce()
var votes = ['tacos','pizza','pizza','tacos','fries','ice cream','ice cream','pizza'];
var initialValue = {}
var reducer = function(tally, vote) {
if (!tally[vote]) {
tally[vote] = 1;
} else {
tally[vote] = tally[vote] + 1;
}
return tally;
}
var result = votes.reduce(reducer, initialValue)
Can someone explain how this is working.
Learning to use Array.reduce()
var votes = ['tacos','pizza','pizza','tacos','fries','ice cream','ice cream','pizza'];
var initialValue = {}
var reducer = function(tally, vote) {
if (!tally[vote]) {
tally[vote] = 1;
} else {
tally[vote] = tally[vote] + 1;
}
return tally;
}
var result = votes.reduce(reducer, initialValue)
Share
Improve this question
asked Aug 17, 2016 at 8:30
aroraarora
8697 silver badges12 bronze badges
2
- meta.stackoverflow./questions/253894/… – Jörg W Mittag Commented Aug 17, 2016 at 8:37
- @JörgWMittag how is this too broad? – arora Commented Aug 17, 2016 at 8:40
6 Answers
Reset to default 3The way how reduce works is pretty similar to map
or filter
. In this case, the reducer is responsible for reducing the array of objects into one object.
The reducer function iterates through all elements of your array. The function is called with two arguments, tally
- result of reducing so far and vote
- array element that is currently being processed.
If tally
does not have a property named just like the element that is currently being processed/reduced, it adds such a key to the object and sets its value to one. Otherwise (key is present), it's incremented by one.
For more information go here
There's plenty of verbose explanations of reduce
here and other places, but I think a simple visual demonstration might help
[1,2,3].reduce(function(a,b) { return a + b }, 0)
// (((0 + 1) + 2) + 3)
// => 6
You can see:
- an input list of 3 elements results in 3 putations
- the initial value is used in the putation with the first element
- the result of one putation is used in the next putation …
- … up until the last putation which is the result of
reduce
Now as for your code, we're not building a sum of numbers, we're building up an Object that holds the tally count of each string in an array.
There's no point in me explaining what everyone else did, but it's not surprising this might've been confusing for you because no one explained that this is abuse of an Object.
In truth, an Object is actually not the best data structure choice for this reduction. Here, an Object is used to emulate the behaviour of a Map.
We'll set initialValue
to a new Map()
and you'll see how the reducer
uses more descriptive syntax within. Note ments aren't even necessary because the code says everything it needs to.
var votes = ['tacos','pizza','pizza','tacos','fries','ice cream','ice cream','pizza']
var initialValue = new Map()
var reducer = function (tally, vote) {
if (tally.has(vote))
return tally.set(vote, tally.get(vote) + 1)
else
return tally.set(vote, 1)
}
var result = votes.reduce(reducer, initialValue)
console.log(Array.from(result.entries()))
Output
[
[ "tacos", 2 ],
[ "pizza", 3 ],
[ "fries", 1 ],
[ "ice cream", 2 ]
]
just for funs
I can show you that the visual representation of the sum via reduce is correct/accurate by writing a reducer that builds a string instead of actually adding the numbers
var initialValue = 0
var reducer = function(a,b) { return '(' + a + ' + ' + b + ')' }
var result = [1,2,3].reduce(reducer, initialValue)
console.log(result)
// (((0 + 1) + 2) + 3)
This technique might be useful for you when trying to understand/debug your own reducers
Basically Array.prototype.reduce()
method applies a function against an accumulator and each value of the array to reduce it to a single value.
In your example the reduced value (the result of a count operation) is assigned as a property of an object called tally
which is returned by .reduce()
;
I ment your with a brief explanation:
// your data in an array
var votes = ['tacos','pizza','pizza','tacos','fries','ice cream','ice cream','pizza'];
// optional value to use as the first argument to the first call of the callback when using Array.prototype.reduce().
var initialValue = {}
// tally = previousValue and vote = currentValue
var reducer = function(tally, vote) {
// if tally is not assign as a key in tally object, add key and add value of one, (basically count 1 for one element in your votes array)
if (!tally[vote]) {
tally[vote] = 1;
} else {
// otherwise if tally object has already this key, increment its value by one, (basically it counts how many times each item in votes array is present in the array)
tally[vote] = tally[vote] + 1;
}
return tally;
}
var result = votes.reduce(reducer, initialValue);
console.log(result);
Notes: you can actually avoid using of declaring a variable for initialValue
and instead using only var result = votes.reduce(reducer, {});
API documentation:
https://developer.mozilla/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
Some more examples and brief explanation:
https://www.airpair./javascript/javascript-array-reduce
I inserted some console.log messages in your code and this helped me understand what was going on.
var votes = ['tacos', 'pizza', 'pizza', 'tacos', 'fries', 'ice cream', 'ice cream', 'pizza'];
var initialValue = {}
var reducer = function(tally, vote) {
console.log("tally: ", tally);
console.log("vote: ", vote);
console.log("tally[vote]: ", tally[vote]);
if (!tally[vote]) {
tally[vote] = 1;
} else {
tally[vote] = tally[vote] + 1;
}
return tally;
}
var result = votes.reduce(reducer, initialValue)
console.log("result: " + JSON.stringify(result));
The reduce() method reduces the array to a single value.
The reduce() method executes a provided function for each value of the array (from left-to-right).
The return value of the function is stored in an accumulator (result/total).
You have this given list:
var votes = ['tacos','pizza','pizza','tacos','fries','ice cream','ice cream','pizza'];
Then you make an Initial List. You can modify this and the function will be applied for both lists.
var initialValue = {}
"reducer" is initialized as a function. The reason that we place the function in "reducer" is so we can call it more easily further on. You can do whatever you want in this function and it will be executed on the list in the uping steps
var reducer = function(tally, vote) {
if (!tally[vote]) {
tally[vote] = 1;
} else {
tally[vote] = tally[vote] + 1;
}
return tally;
}
Finally, the result of the function is stored into the variable.
var result = votes.reduce(reducer, initialValue)
Well, let's try to expand the iteration:
When you execute
votes.reduce(reducer, initialValue)
It actually does this:
reducer(initialValue, votes[0]); // step1, return {'tacos': 1}
reducer(returnedValueOfStep1, votes[1]); // step2, return {'tacos': 1, 'pizza': 1}
reducer(returnedValueOfStep2, votes[2]); // step3, return {'tacos': 1, 'pizza': 2}
reducer(returnedValueOfStep3, votes[3]); // step4 ...
reducer(returnedValueOfStep4, votes[4]); // step5 ...
reducer(returnedValueOfStep5, votes[5]); // step6 ...
reducer(returnedValueOfStep6, votes[6]); // step7 ...
reducer(returnedValueOfStep7, votes[7]); // step8 ...
I think it quite clear right now.
And usually, if we do not provide initialValue as the second parameter for reduce, it will take the first element of array as the initialValue, and starts iteration from the second one:
reducer(votes[0], votes[1]); // step1 ...
reducer(returnedValueOfStep1, votes[2]); // step2 ...
...