Struggling to understand how this code works
function countup(n) {
if (n < 1) {
return [];
} else {
const countArray = countup(n - 1);
countArray.push(n);
return countArray;
}
}
console.log(countup(5));
I can understand why once it gets to the Array.push that it prints out [1], but after that I don't understand how it goes from [1] to [1,2,3,4,5].
Also, wouldn't (n-1) always have to be 1-1 as without it the if (n < 1) won't be true?
It should all become clear if you step through it line by line in a debugger:
I wasn't able to record it as slowly as I would have liked to, because I was running into some max. sizes for creating a GIF from it. It's probably best if you do the same yourself anyway. Watch the call stack and the values of the local variables. When you do this interactively, I'd encourage you to also explore the local variables of the higher-up instances of your function in the call stack (you can click on a call stack entry to switch to its scope).
countup(5) is called, so n = 5. Since 5 is not less than 1, we go to the else branch. With n = 5, we get n - 1 = 4 so countup(4) is called.
Same as #1, but with n = 4, eventually calling countup(3).
Same as #1/#2 several more times until we end up calling countup(0). At this point we have a total of 6 instances of the function in the call stack.
With n = 0, we enter the first branch of the if, returning an empty array [].
The countup(1) instance receives the return value of [] from countup(0) and stores it into countArray.
The countup(1) instance pushes n (1) into countArray, yielding [1]. The array is then returned to the called (countup(2)).
The countup(2) instance receives the return value of [1] from countup(1) and stores it into its own countArray.
The countup(2) instance pushes n (2) into countArray, yielding [1, 2]. The array is then returned to the caller (countup(3)).
Steps #5-8 continue for countup(3), countup(4) and countup(5), until at the end countup(5) pushes 5 into its countArray, ending up with [1, 2, 3, 4, 5], and that array is now returned to the caller (the main function).
The main function got the result [1, 2, 3, 4, 5] from countup(5) which is now passed into console.log.
You can also think about it like this:
countup(0) returns []1.
countup(n) for any nonzero n returns [...countup(n - 1), n]2.
(...where ...array means the spread operator so [a, ...[b, c], d] becomes [a, b, c, d])
So we get the following evolution:
Upwards:
countup(0) = []
\_______________________
\
countup(1) = [...countup(0), 1] = [...[], 1] = [1]
______/
/
countup(2) = [...countup(1), 2] = [...[1], 2] = [1, 2]
____/
/
countup(3) = [...countup(2), 3] = [...[1, 2], 3] = [1, 2, 3]
____/
/
countup(4) = [...countup(3), 4] = [...[1, 2, 3], 4] = [1, 2, 3, 4]
____/
/
countup(5) = [...countup(4), 5] = [...[1, 2, 3, 4], 5] = [1, 2, 3, 4, 5]
Downwards:
countup(5) = [...countup(4), 5]
|············\__ \__
|···············\ \
= [...countup(3), 4, 5]
|············\__ \__ \__
|···············\ \ \
= [...countup(2), 3, 4, 5]
|············\__ \__ \__ \__
|···············\ \ \ \
= [...countup(1), 2, 3, 4, 5]
|············\__ \__ \__ \__ \__
|···············\ \ \ \ \
= [...countup(0), 1, 2, 3, 4, 5]
|··· _______/ | | | | |
|···/ | | | | |
= [...[], 1, 2, 3, 4, 5]
\_x_/ | | | | |
= [ 1, 2, 3, 4, 5]
1: Technically, any n < 1 would make countup(n) return [], not only n = 0.
2: Technically, the same array is used all the time here and just mutated in every step. In a pure functional way of handling this, a copy would have to be created (const countup = n => n < 1 ? [] : [...countup(n - 1), n]). But that doesn't matter for this explanation because the array is of course no longer needed in the previous function after it was returned.
Adding some logging should help you understand the execution sequence. This is triggered initially with countup(5), which then recursively calls countup(n-1) until n-1 is 0. That returns an empty array, and then each previous call of countup appends n to the array and returns it. So you end up with an execution order like:
countup(5)
calls countup(4)
calls countup(3)
calls countup(2)
calls countup(1)
calls countup(0), which returns [] to countup(1)
the call from countup(1) appends 1 to the (empty) array and returns [1] to countup(2)
the call from countup(2) appends 2 to the array and returns [1, 2] to countup(3)
the call from countup(3) appends 3 to the array and returns [1, 2, 3] to countup(3)
the call from countup(4) appends 4 to the array and returns [1, 2, 3, 4] to countup(4)
the call from countup(5) appends 5 to the array and returns [1, 2, 3, 4, 5]
function countup(n) {
console.log('countup('+n+')');
if (n < 1) {
console.log('returning empty array');
return [];
} else {
console.log('calling countup - 1');
const countArray = countup(n - 1);
console.log('pushing '+n);
countArray.push(n);
console.log('returning countArray:');
console.log(countArray);
return countArray;
}
}
console.log(countup(5));
Recursive approaches are interesting but kind of difficult to understand at first glance. In this particular example, the function evaluates for a very specific constraint. If n < 1 the function will return an empty Array.
Let's dive into the code execution:
On the first iteration n = 5 that allow the else block to be executed. Once the second countup call (countup(n - 1)) it evaluates the input again, and since n is still greater than 0 the whole process will repeat itself.
Once the countup function receives an input of 0 (n = 0) it returns an empty array. Such array is then assigned to the countArray variable (in this particular point countArray = []) and the current value of n is pushed into the array (since the n = 0 case already returned, you'll land on the case where n=1). Again this process will repeat itself in every step up until you reach the case where n=5 (technically your first iteration). Once 5 has been pushed into the array, the function will return the array containing every value from 1 to the provided number n sorted from the smallest to the largest number.
Related
const arr = [1, 3, 1, 2, 5, 2, 3, 4, 1, 2, 3, 4, 3]
const resultado = arr.reduce((prev, cur) => ((prev[cur] = prev[cur] + 1 || 1), prev), {})
//resultado = const resultado = { 1: 3, 2: 3, 3: 4, 4: 2, 5: 1,}
I am new to javascript and I need to understand how, in the following code, the array ends in an object.
Create an empty object
For each element in the array:
Assign the value of [the object's value at thee current value plus 1, but if that doesn't exist yet return it the value of 1] to the key of the current index's value
Return the object so that it's available during the next iteration, and at the end for the final index
reduce works by passing in an initial "value" to the callback which acts as the "accumulator". The accumulator will be the first argument to the callback, and the current element in the array iteration will be the second. The accumulator is passed in on each iteration.
In this simple example we're just going to add up some numbers. We pass in 0 as the initial value. This will be passed into the callback as the "accumulator" (acc). c is the current element in the iteration.
On the first iteration acc is 0. On the second iteration it is 1 (0 + 1. On the third iteration it is 3 (1 + 2) - always adding on the value of the current element, and then being passed back into the callback until there are no more elements.
const arr = [1, 2, 3, 4, 5];
const out = arr.reduce((acc, c) => {
return acc + c;
}, 0);
console.log(out);
The example in your question follows the same logic but because it's all on one line it makes it more difficult to understand, so here it is expanded:
Instead of 0 being passed in as an initial value we're passing in an empty object.
If the object has a key that matches the value of the current element add 1 to that property value, otherwise initialise that property value to 1.
Return the accumulator for the next iteration.
const arr = [1, 3, 1, 2, 5, 2, 3, 4, 1, 2, 3, 4, 3];
const resultado = arr.reduce((acc, c) => {
if (acc[c]) {
++acc[c];
} else {
acc[c] = 1;
}
return acc;
}, {});
console.log(resultado);
I met an unexpected result when I count the number of common elements in two arrays. When I use reduce function, it doesn't work. But filter version returns correct result. I want to know what's wrong with my reduce version.
var ls = [1, 2, 3, 4, 5];
var ms = [4, 5, 6, 1, 2];
console.log(ls.reduce((acc, e) => (ms.includes(e) ? 1 + acc : 0), 0));
// 2, incorrect
console.log(ls.filter(e => ms.includes(e)).length);
// 4, correct
Because in your reduce version, when the element is not found, you reset the acc back to zero instead of returning it as is
var ls = [1, 2, 3, 4, 5];
var ms = [4, 5, 6, 1, 2];
console.log(ls.reduce((acc, e) => (ms.includes(e) ? 1 + acc : acc), 0));
// -----------------------------------------------------------^ here
// 4, now corrected
When your callback function that's the first parameter of the reduce function hits the third item in the ls array, "3", it finds that it's not a member of the ms array. This causes the ternary operator to return the right hand expression 0, which resets your accumulator variable, acc. This will restart the count at 0.
Instead, you should return the current value of the accumulator variable instead of 0 like this:
console.log(ls.reduce(acc,e) => (ms.includes(e) ? 1 + acc: acc), 0));
That will give you the correct count of 4 matching elements.
I am trying to get my hand dirty with Javascript
I want to solve the following exercise (found on Codewars):
Given an array of integers, find the one that appears an odd number of times.
There will always be only one integer that appears an odd number of
times.
[7] should return 7, because it occurs 1 time (which is odd). [0]
should return 0, because it occurs 1 time (which is odd). [1,1,2]
should return 2, because it occurs 1 time (which is odd). [0,1,0,1,0]
should return 0, because it occurs 3 times (which is odd).
[1,2,2,3,3,3,4,3,3,3,2,2,1] should return 4, because it appears 1 time
(which is odd).
this is my code:
function findOdd(A) {
const isOdd = (x) => {
return x%2;
}
const counts ={};
for ( i of A)
{
counts[i]= counts[i] ? counts[i] +1:1;
}
const numOccurences = (Object.values(counts));
const occurences = Object.getOwnPropertyNames(counts);
let evenOccurence=0;
let oddOccurence;
for (let y=0;y<numOccurences.length;y++)
{
if(isOdd(numOccurences[y]))
{
console.log("numOccurences[y] is equal to: ",numOccurences[y])
evenOccurence = numOccurences[y];
console.log(`value ${occurences[y]} is appearing ${numOccurences} times which is even`)
}
// }
// console.log("evenOccurence",evenOccurence);
// return evenOccurence;
}
return evenOccurence;
}
// console.log(findOdd([7]));
console.log(findOdd([7,7,8,8,7,5,5,5,5,5,5,5]));
my issue is the value I am putting in evenOccurrence, I would expect an integer, but it comes as 7,3,2 in the array given as an example.
could anyone help me please?
Thanks in advance
You could take a bitwise XOR ^ which wipes out even count values.
const
odd = array => array.reduce((a, b) => a ^ b, 0);
console.log(odd([1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1]));
console.log(odd([7]));
console.log(odd([1, 1, 2]));
Another approach. With an object for keeping the state. The value is either true or false depending on the odd appearance of the value of the array.
Finally take all keys and find the one with the true value.
const
odd = array => {
const states = {};
for (const value of array) states[value] = !states[value];
return +Object.keys(states).find(k => states[k]);
};
console.log(odd([1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1]));
console.log(odd([7]));
console.log(odd([1, 1, 2]));
Updated your answer with comment.
function findOdd(A) {
const isOdd = (x) => {
return x % 2;
}
const counts = {}; // key with will be unique set of A and value will be part will be occurence count
for (i of A) {
counts[i] = counts[i] ? counts[i] + 1 : 1;
}
// const numOccurences = (Object.values(counts)); // her you are just getting list of how many time they occured, without any reference to A or their keys
// const occurences = Object.getOwnPropertyNames(counts);
let evenOccurence = []; // if you want to return a list, create a list and push values in it
for (let key of Object.keys(counts)) { // key is unique value of A
let occurenceCount = counts[key];
if (isOdd(occurenceCount)) {
console.log("numOccurences of key [", key,"] is equal to: ", occurenceCount, ' odd')
} else {
console.log("numOccurences of key [", key,"] is equal to: ", occurenceCount, ' event')
evenOccurence.push(key); }
}
return evenOccurence;
}
console.log(findOdd([7, 7, 8, 8, 7, 5, 5, 5, 5, 5, 5, 5]));
#Nina Scholz's first algorithm is a masterpiece. Basically it is very simple if you are ok with bitwise operations. I just would like to simplify the same thing down to daily math.
var odd = array => array.sort().reduce((a,b,i) => i%2 ? a-b : a+b);
console.log(odd([1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1]));
console.log(odd([7]));
console.log(odd([1, 1, 2]));
I am trying a JavaScript challenge. Not to use standard array reverse method but instead creating a new function to modify an array that given as argument and reverse its elements. Here is the example:
var arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
console.log(arrayValue);
// → [5, 4, 3, 2, 1]
However, I created this function but it didn't work:
function reverseArrayInPlace(arr) {
var newArr = [];
for (var i = arr.length - 1; i >= 0; i--) {
newArr.push(arr[i]);
}
arr = newArr; //This reverse arr successfully but won't work when called
return arr;
}
var arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
console.log(arrayValue);
// → [1, 2, 3, 4, 5], why? The arr variable above returned [5, 4, 3, 2, 1] but not here
This is the answer and it worked:
function reverseArrayInPlace(arr) {
for (var i = 0; i < Math.floor(arr.length / 2); i++) {
var old = arr[i];
arr[i] = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = old;
}
return arr;
}
var arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
console.log(arrayValue);
// → [5, 4, 3, 2, 1]
What is wrong with my method. What I don't get is the console.log did output the right reverse order but it will still show the original arrayValue when output. Can someone explain the difference to me?
The assignment is asking to modify the array in place, not to allocate new array/reassigning reference. This means you have to modify the variable passed as argument without allocating new memory for the variable returned (so you don't have another memory reference). The value you return from your function is not allocated to the original variable as it belongs to another scope and no assignment of returned value is performed (i.e. arrayValue = reverseArrayInPlace(arrayValue))
About in place algorythm, from wikipedia: In computer science, an in-place algorithm is an algorithm which transforms input using no auxiliary data structure. However a small amount of extra storage space is allowed for auxiliary variables. In-place algorithm updates input sequence only through replacement or swapping of elements. -> So you have to modify the original array passed, not to allocate a new one. As arr is inside function it is different from arr outside. The arr argument passed to function (which is a reference, not the whole thing) occupies different memory that arr outside
Your script works. If you try to display it something like this:
<p id="result"></p>
<script>
document.getElementById("result").innerHTML = reverseArrayInPlace(arrayValue);
</script>
it works
Here, see:
https://plnkr.co/edit/GJ57go?p=preview
I am trying to understand a point of confusion I have with JavaScript objects. Specifically, I am interested in finding what, if anything, causes an object reference to break.
To demonstrate the phenomenon, I have included a copy of some output from Chrome's JavaScript console. Note that I am working with arrays here, but we would expect objects to behave similarly given the subtle distinction between arrays and objects in JS. I have added comments for clarity.
// Set x to some array literal
> x = [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
// Set y to x
> y = x
[1, 2, 3, 4, 5]
> x
[1, 2, 3, 4, 5] // as expected
> y
[1, 2, 3, 4, 5] // as expected
As demonstrated above, both x and y output the expected value. Now I shuffle the values of x using a function called shuffle (specified at the bottom of this question).
// Shuffle x
> x = shuffle(x)
[5, 1, 4, 2, 3]
> x
[5, 1, 4, 2, 3] // x changes as expected
> y
[5, 1, 4, 2, 3] // y changes as expected
Again, everything works as expected above. The variables x and y have maintained reference to the same object. However, when we repeat this operation, the results are strange.
// Shuffle x
> x = shuffle(x)
[3, 1, 5, 4, 2]
> x
[3, 1, 5, 4, 2] // x changes as expected
> y
[5, 1, 4, 2, 3] // y didn't change this time
Below is the shuffle function, adapted from here. Its purpose is to shuffle the contents of an array (parameter r1) and to return the first n items of the mixed array.
function shuffle(r1,n) {
var i = r1.length, j, tempi, tempj, r2;
r2 = r1;
while (--i) {
j = Math.floor(Math.random() * (i + 1));
tempi = r2[i];
tempj = r2[j];
r2[i] = tempj;
r2[j] = tempi;
}
return r2.slice(0,n);
}
I have since fixed the problem by rewriting my shuffle function based on this function. However, I would still like to understand what's going on. For a quick look at the code in action, I have made a jsFiddle.
Any ideas? I appreciate your time.
If you remove the .slice(0,n);, it will behave the way you expect. slice makes a new array.
So the first time you call shuffle, within your loop you modify the array x = y = r1 = r2. Then you make a copy of it on that last line and assign that to x. Now x !== y, but they contain the exact same elements. You can test that they are distinct objects after your first call to shuffle:.
The next time you call shuffle you are shuffling the copy of x you made and y is untouched.
.slice() makes a shallow copy of the Array, and so you're overwriting x with a new Array.
// The original was shuffled, but now `x` is a new Array
x = shuffle(x);
That's why y showed the first shuffle (because you hadn't sliced it yet), but none thereafter. The subsequent shuffle was on the overwritten x, and y still references the original.
If you wanted to truncate the original Array, just change its .length.
So instead of this:
return r2.slice(0,n);
Do this:
r2.length = n;
...though you're not passing anything to n currently.