JavaScript: Writing a reduce function, trouble with callback function that subtracts values - javascript

I'm working to solve a code challenge in JavaScript where I am tasked with creating a function, called reduce, that will reduce a collection to a value which is the accumulated result of calling a function on each item over which I've iterated. The three parameters to include are array, callback, and start. Start is the index at which to begin, and if not provided as an argument defaults to the index zero.
Below is what I have currently:
function reduce(array, callback, start) {
if(typeof start === 'number'){
var initVal = start;
} else {
var initVal = 0;
}
return array.reduce(callback, initVal);
}
I have some tests against which I can test my solution, and my current work is failing only one test involving a callback function that subtracts the values. Here is the test I'm currently failing:
var difference = function(tally, item) {return tally - item; };
var total = reduce([1, 2, 3], difference);
expect(total).to.equal(-4);
Guidance is greatly appreciated.
Edited to add working solution:
function reduce(array, callback, start) {
if( typeof start === 'undefined' ){
return array.reduce(callback);
} else {
return array.reduce(callback, start);
}
}

Your code does not work since you're misusing the start which is intended to be an element index to start with with the initial value.
So for the [1, 2, 3] array as an input and the subtraction function as a callback what you are getting is:
The reducer accumulator is initialised with 0 (what you are supposed to not do actually)
0 - 1 -> -1
-1 - 2 -> -3
-3 - 3 -> -6
What you should have done instead:
There is no third parameter passed, so you're iterating from the beginning of the array.
Accumulator is not initialised explicitly, so the first element is passed to it, hence acc = 1
1 - 2 -> -1
-1 - 3 -> -4

Related

Why counter increases and fn is called? How it works?

I solved kata on codewars. Acctually I did this by accident and I don't understand why this code works. May you explain it?
Kata:
Write a function, persistence, that takes in a positive parameter num and returns its multiplicative persistence, which is the number of times you must multiply the digits in num until you reach a single digit.
39 --> 3 (because 39 = 27, 27 = 14, 1*4 = 4 and 4 has only one digit)
My solution:
function persistence(num) {
let count = 0;
const arr = [...num.toString()];
const sumArr = arr.reduce((res, val) => (res *= val));
if (arr.length > 1) {
**// Why? How this line works?
// Why it doesn't crashes?
// Why it returns correct counter value and calls the function?**
count += 1 + persistence(sumArr)
}
return count;
}
persistence(39); //3
Why if I do like this, the counter do not save the result:
if (arr.length > 1) {
count += 1
persistence(sumArr) }
Basically, the persistence(sumArr) function acts as a recursive function and returns count of 0 for a base condition that is when the number is single digit. For 39, count is 1+persistence(27)
For 27, count is 1+persistence(14) and for 14, 1+ persistence (4) which is nothing but 1+0 and recursively it adds up to be 3
count is a local variable, only reachable within the scope of persistence(), so when you do a recursive loop, you create a new variable called count but it's in a new scope - the method that you called - it just happens to have the same name as persistence().
It would be totally different if count were a global variable, outside of the scope of the method.
let count = 0;
function persistence(num) {
...
}
Calling persistence() again within persistence() would then use the same variable, not a new one.
This is recursion!
Whenever a function is called by the caller, that function (a.k.a callee) is pushed into the call stack. All the arguments, local variables needed for that function is present there. This is also known as stack frame. The callee is popped out of the stack when it returns to the caller.
In your case both the caller and the callee happened to be the same function. This is called recursion. During recursion, variables local to that function aren't shared. A new stack frame is set up for each callee.
if (arr.length > 1) {
count += 1
persistence(sumArr)
}
Here, the count that you are returning from the callee isn't getting added up with the count of the caller.
If we visualize what is happening then:
persistence(37) = 1 + persistence(27) = 1 + 1 + 1 + 0 // [putting value returned from persistence(27)]
persistence(27) = 1 + persistence(14) = 1 + 1 + 0 // [putting value returned from persistence(14)]
persistence(14) = 1 + persistence(4) = 1 + 0 // [putting value returned from persistence(4)]
persistence(4) = 0

Reduce method not clear

So, I started playing with reduce() and I realised I can pass an Object as the first element of the method and I saw a couple of examples and this is one of them.
const arr = ['y', 'n', 'y', 'y', 'n'];
let test = arr2.reduce((sum, val) => {
sum[val] = (sum[val] || 0) + 1;
console.log('sum of val', sum[val], 'value', val)
return sum;
}, {})
I added that console log so I can see what is going on, but I cant figure it out. HOW DOES THE METHOD KNOW? How does it add that val (which is n or y) in the object and followed by it add the sum of how many of identical elements exists in the array. How does that sum become for example {y: 20} - assuming there are 20 y's in an array.
Im confused by it, at first it seemed simple but I guess its not.
It doesn't "know", you do it, here: sum[val] = (sum[val] || 0) + 1;
sum is the empty object in the first iteration, and then it's the one returned from the previous iteration (which is the same, due to return sum;). And val is the current value ('y' or 'n').
So, in the first iteration, sum will be {} and val will be 'y'. This line will then set sum['y'] = 1 because it essentially does sum['y'] = (sum['y'] || 0) + 1 - and sum['y'] is undefined at that point, so you'll have (undefined || 0) + 1 which is 0 + 1 which is 1.
The next time the same happens for 'n'.
And the third time, sum['y'] will be already 1 from before, so that expression becomes (1 || 0) + 1 which is 1 + 1 which is 2, so you get sum['y'] = 2.
And so on.
See this screencast from a debugger: https://recordit.co/FVkXjW1b5y
.reduce() takes two arguments
callback function
initial value (optional)
In your case, empty object literal {} is the initial value. If initial value is supplied, it is passed as the first argument to the callback function.
Second argument to the callback function of .reduce() is the current element of the array on which .reduce() is called. In first iteration, val is the first element in the arr array, i.e. 'y'.
sum[val] = (sum[val] || 0) + 1;
In each iteration, above statement will add value of val as a key in the sum object (initial value) and its value is 0 + 1 if sum[val] is undefined or sum[val] + 1 if sum[val] is defined.
How your code is executing:
When callback is called for the first time, sum is {} and val is 'y'. Since sum[val] or sum['y'] is undefined, 'y' is added as a key in sum and its value is 1. Same thing happens when callback function is called second time. After 2 calls, sum looks like { y: 1, n: 1 }.
In third call, since sum[val] is equal to 1, so previously added key y is overwritten with sum[val] + 1 which evaluates to 1 + 1. So after third call, sum looks like { y: 2, n: 1 }. Same thing happens in the subsequent calls to callback function.

Javascript reduce gotcha - skips first iteration?

Why does javascript's implementation of reduce skip execution for the first iteration?
[1,2,3].reduce((acc, val) => {
console.log('acc',acc);
console.log('val',val)
return acc + val;
});
// acc 1
// val 2
// acc 3
// val 3
// 6
I notice that the first statement execution never runs (in this case, I would have expected there to be 6 console logs, 2 for each element). This was very unexpected behavior when I was trying to execute a function with a side effect within each iteration with reduce.
In other languages that I've used, every iteration of the list passed executes. Are there examples otherwise?
Why does this happen and why is the implementation of javascript's native Array reduce like this?
========================= EDIT 1/Solution ========================
To make sure it goes through the first iteration, give it an initial value (the 2nd argument here/ 0 in this case)
[1,2,3].reduce((acc, val) => {
console.log('acc',acc);
console.log('val',val)
return acc + val;
}, 0);
That is because of the fact that on every iteration, the first value is treated as a return value (or the accumulator).
Straight from here, you can see
The accumulator accumulates the callback's return values; it is the
accumulated value previously returned in the last invocation of the
callback, or initialValue, if supplied (see below).
If we look at the source code here, we can see how it's implemented:
Array.prototype.myReduce = function(callback, initialVal) {
var accumulator = (initialVal === undefined) ? undefined : initialVal;
for (var i = 0; i < this.length; i++) {
if (accumulator !== undefined)
accumulator = callback.call(undefined, accumulator, this[i], i, this);
else
accumulator = this[i];
}
return accumulator;
};
In the else structure, we can see that if the value is undefined, we set it to the i-th subindex in the array; which, for the first iteration, is the first one. After that, it becomes the callback (return) value of the iterations which follow.
If you want, you can backtrack and check the output.

recursive function return different values depending of order when summing array elements

I implemented the following recursive JS function to get the the sum of elements in a array. This function works fine, when input [1,2,3] it returns 6, which is OK.
function sumOfNumbers(array) {
if (array.length == 1) {
return array[0];
} else {
last = array.length - 1;
return array[last] + sumOfNumbers(array.slice(0, last));
}
}
However, when changing the order of the sum to:
return sumOfNumbers(array.slice(0,last)) + array[last];
It returns 5 for [1,2,3]. Does anybody knows why?
Because the variable last is global, and you're changing it with your call to sumOfNumbers(array.slice(0,last)), before this part: array[last] sees it.

Underscore reduce, about memo

According the documentation underscore-reduce I should pass three parameters.
For example:
var m = _.reduce([1,2,3], function (memo, num) {return (num * 2) +memo }, 0);
m; // 12 as expected
If I try to pass just the first two parameters I get a different value. Why?
var m = _.reduce([1,2,3], function (memo, num) {return (num * 2) +memo });
m; // 11 ..why?
With only two parameters passed into reduce, it will use the first and second array items as arguments to the first function call.
function addDouble(memo, num) {return (num * 2) +memo }
[1,2,3].reduce(addDouble, 0)
// is equivalent to
addDouble(addDouble(addDouble(0, 1), 2), 3)
[1,2,3].reduce(addDouble)
// is equivalent to
addDouble(addDouble(1, 2), 3)
Usually you will pass the start value, but many operations have the same result when starting without their identity element. For example:
function add(a, b) { return a+b; }
function double(a) { return 2*a; }
[1,2,3].map(double).reduce(add) == [1,2,3].map(double).reduce(add, 0)
See also the docs for native reduce().
If you just pass two parameters, the initial memo will take the first value of array, and the go through the rest. 11 = 1 + (2 * 2) + (3*3). this is why.
And if you pass three parameters, the memo will take the third parameter as initial meno and go through each items of the array.

Categories