How are parameters get mapped in reduce function? - javascript

The MDN defines reduce() as below:
The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.
Let's not take anything for granted and just analyse the syntax of below statements:
const array1 = [1, 2, 3, 4];
const reducer = (y, x) => y + x;
// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10
// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
reducer is a function that takes two parameters and return their sum
reducer is executed on each element of the array, but 'each element of the array' is only ONE parameter, why can i assume that the SUM is the other parameter and cached somewhere waiting for the next add operation? and why can i assume reduce is returning the sum at the end?
Could someone answer these questions? From someone coming from other language background e.g. C which also has function concept. I am often confused by Javascript's syntax.
and that's how i get more confused when i see:
const pipeline = [
array => { array.pop(); return array; },
array => array.reverse()
];
pipeline.reduce((xs, f) => f(xs), [1, 2, 3]);
because again, according to MDN, The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.
only this time,
reducer: (xs, f) => f(xs)
accumulator: [1, 2, 3]
array1: pipeline
then how do we explain its behaviour similar to our first example in English?

reducer is executed on each element of the array, but 'each element of the array' is only ONE parameter, why can i assume that the SUM is the other parameter and cached somewhere waiting for the next add operation?
The callback provided is called multiple times, once for each item in the array (or, for length - 1 times, in case no initial value is provided). You could easily implement this yourself:
const array1 = [1, 2, 3, 4];
const reducer = (y, x) => y + x;
Array.prototype.myReduce = function(callback, initialValue) {
let accum = initialValue === undefined
? this.shift()
: initialValue;
for (let i = 0; i < this.length; i++) {
accum = callback(accum, this[i], i, this);
}
return accum;
}
// 1 + 2 + 3 + 4
console.log(array1.myReduce(reducer));
// expected output: 10
// 5 + 1 + 2 + 3 + 4
console.log(array1.myReduce(reducer, 5));
Just because you pass one function to .reduce (or to any other function) doesn't put a limit on how many times that function can be called.
and why can i assume reduce is returning the sum at the end?
That's just how the method is defined - like with the implementation above, the accumulator (or accum) gets reassigned for every iteration, and passed to the next invocation of the callback.
The browser's native implementation of the method isn't actually written in Javascript like above, but its functionality is the same (for the most part).
The pipeline works the same way. For every element of the array provided, the accumulator is reassigned, and the next element is called with the new accumulator. Here, there's an array of functions which are being called, and each function's return value is being used as the next accumulator, and the value returned by the last function call is what the whole .reduce call resolves to.
const pipeline = [
array => { array.pop(); return array; },
array => array.reverse()
];
pipeline.reduce((xs, f) => f(xs), [1, 2, 3]);
Item 1: initial value (accumulator) is [1, 2, 3]. Plugging into array => { array.pop(); return array; } and you .pop() its last value (the 3, resulting in [1, 2], then you return the array.
Item 2: Accumulator (return value of last iteration) is [1, 2]. Plug it into array => array.reverse(), and you get the same array, reversed: [2, 1].
There are no more items in the array, so this [2, 1] is the value that the whole reduce call evaluates to.

Related

Problem with getting javascript to forget a variables value

I'm trying to solve a coding challenge
it gives me an array and waits for the answer ,then gives me the next array and so on.
https://www.codewars.com/kata/5648b12ce68d9daa6b000099/train/javascript
I am trying to take this input: var busStops = [[10,0],[3,5],[5,8]] and return 5.
the code is supposed to add the first in each pair to a total while subtracting the second in each pair from the total eg: 10 - 0 + 3 - 5 + 5 - 8 = 5
First my code loops through the inner arrays and outer array ,pushing it into myarr as a regular array eg: [10,0,3,5,5,8].
It then adds the value if it is index is 0 or even and subtracts it if the index is odd.
This actually works!
Until it is given a second array eg: [[3,0],[9,1],[4,10],[12,2],[6,1],[7,10]]
It is still calculating the total correctly but is still remembering the total from the first array meaning it is returning 22 instead of 17
Why?
There is a var answer = 0 that is being executed ahead of the second loop
It should forget the value of the previous answer.
Right?
Edit: I figured out my problem. I just needed to empty myarr after the total was calculated!
let myarr = [];
var number = function (busStops) {
for (var i = 0; i < busStops.length; i++) {
for (var j = 0; j < busStops[i].length; j++) {
/*console.log(busStops[i][j]);*/
myarr.push(busStops[i][j]);
}
}
console.log("test");
var answer = 0;
console.log("test again");
for (let t = 0; t < myarr.length; t++) {
if (t == 0 || t % 2 == 0) {
answer = answer + myarr[t];
} else {
answer = answer - myarr[t];
}
}
console.log(answer);
return answer;
};
The task at your hand tries to somehow find a value (an integer) from an array of arrays (multidimensional array). That task seems to be reducing that multidimensional array into a single integer.
Luckily, JavaScript has some powerful array methods and one of them is the reduce method:
The reduce() method executes a user-supplied "reducer" callback function on each element of the array, in order, passing in the return value from the calculation on the preceding element. The final result of running the reducer across all elements of the array is a single value. Source: MDN
The reduce method tries to reduce an array's elements into a single value and in your case we want to reduce that multidimensional array into a single value that is the number persons who are still in the bus.
Before typing some code, let's dig a bit deeper into the reduce method:
It accepts 2 parameters, a callback function that acts as the reducer and the initial value to be used in the first iteration of the reduce method.
The reducer callback function, on its own, accepts 4 parameters that are supplied by the reduce method. You may learn more about those parameters here as am only going to focus on the first 2 parameters the reducer accepts:
previousValue: hold the value from the previous reducer call. On first call, it contains the value you set to the initial value parameter of the reduce method or, if you didn't supply an initial value, previousValue shall hold the value of your array's first element (arr[0]).
currentValue: hold the current reduce iteration's item.
Now, let's get back to the task we have, we need to calculate the number of persons who are still in the bus based on a supplied multidimensional array. Each item in that multidimensional array is an array of two values where the result we need at the end is: the sum of the differences between each array, in the multidimensional array, first and second values (sum = multiDim[i][0] - multiDim[i][1] + multiDim[i + 1][0] + multiDim[i + 1][1] etc...).
To solve this task, we'll reduce that multidimensional array into a single number (let's call it result) by using a simple reducer function that will start by an initial value of 0 (as we're calculating a sum in our case) and will add, to the result, the difference between the first and the second values of the array supplied by the reduce at each iteration.
To illustrate, here's a live demo:
/**
* a function that calculates and returns the number of person who are still in the bus or precisely, the sum of the differences between "c[0]" and "c[1]"
* busArray: the supplied multidimensional array to reduce
* the reducer accepts two parameters:
* r: the result from the last call to the reducer function (initially set to 0, the initial value (see second parameter passed to the "reduce" method))
c: hold the current iteration's array.
*/
const calculateWhoAreInTheBus = busArray => busArray.reduce((r, c) => r += c[0] - c[1], 0);
/** test the created "calculateWhoAreInTheBus" function */
console.log(calculateWhoAreInTheBus([
[10, 0],
[3, 5],
[5, 8]
])); // should print: 5
console.log(calculateWhoAreInTheBus([
[3, 0],
[9, 1],
[4, 10],
[12, 2],
[6, 1],
[7, 10]
])); // should print: 17
console.log(calculateWhoAreInTheBus([
[3, 0],
[9, 1],
[4, 8],
[12, 2],
[6, 1],
[7, 8]
])); // should print: 21
console.log(calculateWhoAreInTheBus([
[0, 0],
[0, 0]
])); // should print: 0
I would advice you to use Array.prototype.reduce instead. For example like this:
const reducer = (previous, current) => previous + current[0] - current[1];
const answer = busStops.reduce(reducer, 0);
It is very brief (although this is not a goal in and of itself) and the reducer function does almost trivial work, so it does not complicate unneccesarily. Best of all it encapsulates the functionality with a minimal need of extra variables.
Othwerwise you could simplify your function a bit but use the let keyword to keep variables locked to scope like:
function number(busStops) {
let answer = 0;
for (let bs of busStops) {
answer += bs[0] - bs[1];
}
return answer;
}

Why map or reduce keeps running without any condition given?

const array = [7, 2, 4, 1, 10, 6, 5, 11]
const max = array.reduce((acc, val) => {
console.log(val, acc)
return val > acc ? val : acc
}, 0)
console.log(max)
I was looking at this code of reduce array method, one thing I couldn't understand at all is, How the reducer function is going to the next iteration? There is no condition that forces the reducer function to go to the next element in the array. In the first iteration, the val is 7, the first element of the array, and acc is 0, the reducer function returns 7 as per the condition written.
My question is how the number 7 as being the new accumulator is going to be called on the reducer function. I thought the normal procedure is you have to meet some kind of condition to iterate over again and again. Is there something written in the reduce method itself? Can you explain me please?
Note that array.reduce:
reduce
calls the callback, as a function, once for each element after the
first element present in the array, in ascending order.
You could understand the reduce as a array.map but the goal of it is to change the array to a singe output.
It will loop over the whole array same with the forEach/map/...
Check below example, even though you don't do anything, like return or anything else to array.reduce, it will still work and iterate the array
You could check here for more
But of course if you don't use return for array.reduce, there will be no benefit for you to use array.reduce
const array = [7, 2, 4, 1, 10, 6, 5, 11]
const max = array.reduce((acc, val) => {
console.log(val)
}, 0)
console.log(max)
As per the docs here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
The reduce() method executes a user-supplied “reducer” callback function on each element of the array,
The reduce method's implementation resembles something like this:
Array.prototype.reduce = function(callback, initialValue) {
var acc = initialValue;
for(var i = 0; i < this.length; i++) {
acc = callback(acc, this[i], i);
}
return acc;
}
The reduce methods iteration condition is the array length.
The same goes for map.

What is the reason array is available inside reduce(), map(), etc.?

In the following example, we have access to the array as numbers and arr. It seems more in line with functional programming to use the internal variable arr but what is an explicit reason why we should use it instead of the exterior variable, since, numbers and arr are both pointers to the same array value anyway.
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, m, index, arr) => {
console.log(`acc=${acc}, m=${m}, index=${index}, arr=${arr}`);
console.log(`acc=${acc}, m=${m}, index=${index}, numbers=${numbers}`);
return acc += m;
}, 100);
console.log(sum);
Because not every array will be stored in a variable. You can chain calls to map() & other, or after a call to a function that returns an array, in those cases you can access the array by variable name.
functionThatReturnsAnArray(...).map((acc, m, index, arr) => {
// We can only access the array because
//it was passed as an argument to the anonymous function
})

How do these extra parameters work with map()?

I'm working on better understanding functional programming in javascript, but I'm a bit confused by what I've seen fed to map functions. Take the example below:
const f = x => (a, b, c) => b + a;
const arr = [1, 2, 3, 4, 5, 6];
const m = arr.map(f(1));
document.write(m);
When f returns a it will print each value, as expected. If it returns b it seems to return the index, and c will return the entire array for each value. Is there a reason to why this function works this way?
Array.prototype.map() callback function has three default parameters
The current element of the iteration
The index of the current element of the iteration
The array that .map() function was called upon
f returns a function which is set as callback of .map()
See also Array.from()
In your example, You are invoking map with a 3-ary callback where:
a -> current element
b -> current index
c -> original array
and returning c. Therefore, your result will be a new array containing a reference to the original array for every element iterated over.
Since you aren't doing anything with x, there is no need for a nested function here. A better example of how you can use this concept would be something like:
const add = a => b => a + b
const arr = [1, 2, 3, 4]
const newArr = arr.map(add(3))
// [4, 5, 6, 7]
console.log(newArr)

Flattening Array In JavaScript- Explanation needed

I'm reading a book called Eloquent JavaScript. There's an exercise in it that requires one to flatten a heterogeneous array & after trying so long and failing to get the answer, I looked up the solution online & couldn't understand the code. I'm hoping someone will be kind enough to explain, especially for argument "flat" and how it's supposed to work. The code is below
var arrays = [[1, 2, 3], [4, 5], [6]];
console.log(arrays.reduce(function(flat, current) {
return flat.concat(current);
}, []));
The reduce function defined in the book is:
function reduce(array, combine, start) {
var current = start;
for (var i = 0; i < array.length; i++)
current = combine(current, array[i]);
return current;
}
and as a method of an array,
arr.reduce(combine, start);
Let's look at each part of the reduce method. The book describes it as "folding up the array, one element at a time." The first argument for reduce is the "combiner function", that accepts two arguments, the "current" value and the "next" item in the array.
Now, the initial "current" value is given as the second argument of the reduce function, and in the solution of flattening arrays, it is the empty array, []. Note that in the beginning, the "next" item in the array is the 0th item.
Quoting the book to observe: "If your array contains at least one element, you are allowed to leave off the start argument."
It may also be confusing that in the flattening solution, current is placed as the second argument to reduce, whereas in the reduce definition above, current is used to assign the cumulative, folded value. In the flattening solution, current refers to the "next" arrays item (the individual array of integers)
Now, at each step of the reduction, the "current" value plus the next array item is fed to the (anonymous) combiner, and the return value becomes the updated "current" value. That is, we consumed an element of the array and continue with the next item.
flat is merely the name given to the accumulated result. Because we wish to return a flat array, it is an appropriate name. Because an array has the concat function, the first step of the reduce function is, (pretending that I can assign the internal variables)
flat = []; // (assignment by being the second argument to reduce)
Now, walk through the reduction as iterating over arrays, by going through the steps shown above in reduce's definition
for (var i = 0; i < arrays.length; i++)
flat = combine(flat, arrays[i]);
Calling combine gives [].concat([1, 2, 3]) // => [1, 2, 3]
Then,
flat = [1, 2, 3].concat([4, 5]) // => [1, 2, 3, 4, 5]
and again for the next iteration of the reduction. The final return value of the reduce function is then the final value of flat.
This would be the solution I came with with ES6 format:
const reduced = arrays.reduce((result,array) => result.concat(array),[]);
console.log(reduced);
I have implemented this solution and this seems to work for nested arrays as well.
function flattenArray(arr){
for(var i=0;i<arr.length;i++){
if(arr[i] instanceof Array){
Array.prototype.splice.apply(arr,[i,1].concat(arr[i]))
}
}
return arr;
}
There is an easy way to do these exercises. those functions are already built inside the javascript so you can use them easily.
But the whole joy of this exercise is to create those functions:
Create reduce function. Reduce function should add all array elements. you can use a higher-order function or just a normal one. here is an example for higher-order:
function reduce(array, calculate){
let sumOfElements = 0;
for(let element of array){
sumOfElements = calculate(sumOfElements, element)
}
return sumOfElements
}
Next step is to create a concat function. since we need to return those reduced arrays in new array we will just return them. (Warning: you must use rest parameter)
function concat(...arr){
return arr
}
And for last. you will just display it (You can use any example)
console.log(concat(reduce([1, 2, 3, 4], (a, b) => a + b), reduce([5, 6], (a, b) => a + b)))
The reduce method acts as a for loop iterating over each element in an array. The solution takes each array element and concatenates it to the next one. That should flatten the array.
var arr =[[1,2],[3,4],[5,6]]
function flatten(arr){
const flat= arr.reduce((accumulator,currentValue)=>{
return accumulator.concat(currentValue)
})
return flat
}
console.log(flatten(arr))
//Output 1,2,3,4,5,6

Categories