Javascript recursion related question about push() and unshift() methods working opposite - javascript

function countup(n) {
if (n < 1) {
return [];
} else {
const countArray = countup(n - 1);
countArray.push(n);
return countArray;
}
}
console.log(countup(5));
After running the above code it returns an array: [1, 2, 3, 4, 5], but push() adds new values at the end of an array so when value of n was 5 it should push 5 to the end of the array and when value of n got 4 it should push 4 at the end of the array like [5,4].
So why not it is returning [5,4,3,2,1] ? It is hard to understand what is happening in this code probably because of this recursion. Isn’t unshift() (which add new values to start of the array) should return [1,2,3,4,5] and push() [5,4,3,2,1] why opposite is happening?

As #Joseph stated in a comment the second to last function call would get pushed to the array first, then it would return that array, where it immediately adds the next number up the call stack.
Here are the steps being taken.
Initial call enters itself recursively n times, where the bottom call returns [] and then [1], [1, 2] ... [1, 2, ..., n] all the way up the call stack where upon the first function call ends and the program does something else.
To get [n, ..., 2, 1] you need to use the Array.prototype.unshift() method, which takes any javascript primitive type, ie String, Number, Boolean, and Symbols, in a comma separated format, like countArray.unshift(4, 5) or countArray.unshift(...anotherArray), and adds them to the beginning of the array.
ie
let someArr = [3, 2, 1];
someArr.unshift(5, 4);
console.log(JSON.stringify(someArr));
// outputs [5, 4, 3, 2, 1]
or
let someArr = [1, 2, 3];
let anotherArr = [5, 4]
someArr.unshift(...anotherArr);
console.log(someArr);
// outputs [5, 4, 1, 2, 3]
Where the output of
function countup(n) {
if (n < 1) {
return [];
} else {
const countArray = countup(n - 1);
countArray.unshift(n);
return countArray;
}
}
console.log(countup(5));
will be [5, 4, 3, 2, 1] tested with node in Vscode.

One useful way to think about this is to start by imagining you already had a function that does what you want for lower values, and then see how you would write one that works for higher values. That imaginary function should be a black box. All we should know is that it does what we want in the case of lower values. We don't care about its implementation details.
So lets say we had a function imaginaryBlackBox, and we knew that it returned the correct countUp values that we want. So, for instance, we know that imaginaryBlackBox (4) returns [1, 2, 3, 4].
Now, knowing that, how might we write a function that works for an input of 5 as well? How about something like this:
function countup(n) {
if (n < 1) {
return [];
} else {
const countArray = imaginaryBlackBox(n - 1);
countArray.push(n);
return countArray;
}
}
Again, we don't know how imaginaryBlackBox works. We just know that it returns the correct result for lower values of n. Our base case remains obvious. For another case, some n greater than 0, we call imaginaryBlackBox(n - 1), and by our basic assumption, we know that will return [1, 2, 3, ..., (n - 1)], which we store in countArray. Then we push n onto that array, to end up with [1, 2, 3, ..., (n - 1), n]. We return that value and we're done.
Now here's the trick. We know an implementation of imaginaryBlackBox -- it's the function we're writing! So we can simply replace it with countUp and know it will work.
function countup(n) {
if (n < 1) {
return [];
} else {
const countArray = countUp(n - 1);
countArray.push(n);
return countArray;
}
}
This required a few assumptions, and they are important to all recursive functions:
There is at least one base case whose value we can calculate without any recursive call. Here, when n is < 1, we simply return [].
For other cases, we can break down our problem into one or more recursive cases, where the input is in some clear and measurable way closer to a base case, so that subsequent steps will hit a base case in a finite number of calls. Here we reduce n by 1 on every step, so eventually it will be below 1.
Our function is effectively pure: its outputs depend only on its inputs. That means we can't count on changing a global variable or reading from one that might be changed elsewhere. Note that I use the qualifier "effectively" here; it doesn't matter if this function has observable side-effects such as logging to the console, so long as its output is dependent only on its input.
Anytime you have those conditions, you have the makings of a good recursive function.

Related

Recursion better understanding

https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/basic-javascript/use-recursion-to-create-a-countdown
We have defined a function called countdown with one parameter (n). The function should use recursion to return an array containing the integers n through 1 based on the n parameter. If the function is called with a number less than 1, the function should return an empty array. For example, calling this function with n = 5 should return the array [5, 4, 3, 2, 1]. Your function must use recursion by calling itself and must not use loops of any kind.
// Only change code below this line
function countdown(n){
if (n<1)
return [];
else{
const numbArray = countdown(n-1);
numbArray.push(n)
return numbArray;
}
}
console.log(countdown(5))
// Only change code above this line
comes out like [1 2 3 4 5] instead of [5 4 3 2 1]
I've been breaking my mind forever then I have completed it with the unshift method but felt like it wanted to me use push even though that sends data to the end of the stack
function countdown(max, currentN) {
if (currentN > max)
return [];
else if (currentN <= max) {
const numbArray = countdown(max, currentN + 1);
numbArray.push(currentN)
return numbArray;
}
}
console.log(countdown(5, 1))
// Only change code above this line
basically it just counts up to the end value given at start
else {
const arr = []
arr.push(n)
return arr.concat(countdown(n-1))
}
you can use this code replace, y question reason is y not understand recursion, try to break the point and watch the function execution step by step in the browser or vscode

Recursion Issue(I just can't understand what it is doing)

I have been reading multiple articles and as soon as I go to do something recursively I just don't get it, I understand the base case, but I don't get the recursive step. so I have this code which is a solution I found.
function range (start, end) {
if (end < start) return [];
if(start == end) {
return [start];
}
else {
const numbers = range(start , end - 1);
numbers.push(end)
return numbers;
}
}
so, I understand the start == end, here is what I do not get.
why is numbers an array? I don't see anywhere that range is an array, I also don't understand if I use start + 1 and push start, the numbers are backward. I have spent 3 weeks so far trying to get a better grasp on recursion and just can not do it, if someone could please help explain to me and maybe offer some resources I have not found? I watched Al Sweigarts recursion video, and thought I understood it until I went to do something, I found it its the recursive step that is just destroying me, I am trying everything I can to understand and get an idea of how this works but I am just super frustrated with myself at this point. thank you for any help.
The only possible thing range is returning here is [] which is an array and [start] which is also an array. So when youre calling range recursively from the else block then you'll always end up with an array since there arent any other paths the code can go.
See comments inline:
function range (start, end) {
console.log("range function is running with arguments of: ", start, end);
// If end < start, exit function and return an empty array
if (end < start) return [];
// If not, check if start and end are equal (with conversion)
if(start == end) {
// If so, return an array that only contains the start parameter value and exit
return [start];
} else {
// If the above tests are false, call the function again
// but with end being one less than before and assign either
// array from above as the value of numbers.
const numbers = range(start , end - 1);
numbers.push(end); // Add end to the end of the array
return numbers; // Return this array instead of the other 2
}
}
console.log("Final array is: ",range(10,20)); // Display the final returned array
domain and codomain
A function's inputs are known as its domain and the function's return type is known as its codomain. These types can effectively guide us when writing our implementation -
range : (number, number) -> number array
fill in the blanks
This helpful template can get you started on almost any recursive function. It may seem challenging at first, but things become easier as we fill in the pieces. According to the types for range, we already know 2 and 3 must return some array -
// range : (number, number) -> number array
function range(start, end) {
if (...) // 1
return ... // 2 (must return array)
else
return ... // 3 (must return array)
}
1. base case
Each recursive function needs a base case, which does not result in a recursive call, allowing the function to eventually exit. Under what conditions does the range function exit?
// range : (number, number) -> number array
function range(start, end) {
if (end < start) // 1 ✅ when end subceeds start, exit
return ... // 2
else
return ... // 3
}
2. base value
When the exit condition is met, we need to return the base value. Typically it is the empty value of our function's return type, or codomain. Since we must return an array, what is the empty value for arrays?
// range : (number, number) -> number array
function range(start, end) {
if (end < start) // 1 ✅
return [] // 2 ✅ an empty array is []
else
return ...
}
3. inductive case(s)
We must specify what happens when the exit condition is not met in the else branch, known as the inductive cases. If end < start is not true, by induction we know that end >= start. In this case we solve the problem for the current start and end, and append it to the result of the smaller sub-problem. In other words, if I have range(P,Q) the answer is -
append Q to the result of range(P, Q - 1)
Remember the type for range gives us a hint. When range is called with new arguments, the first and second arguments are both number. The return value will be an array of numbers, or number array.
In JavaScript we write that as -
// range : (number, number) -> number array
function range(start, end) {
if (end < start) // 1 ✅
return [] // 2 ✅
else
return [...range(start, end - 1), end] // 3 ✅ minus and append
}
substitution model
Recursion is a functional heritage and so using it with functional style yields the best results. This means writing functions with referential transparency, meaning there are no observable side-effects and the function will always return the same value when the same inputs are used. Using the substitution model we can substitute any function call for its return value -
range(3,6)
[...range(3, 6 - 1), 6]
[...range(3, 5), 6]
[...[...range(3, 5 - 1), 5], 6]
[...[...range(3, 4), 5], 6]
[...[...[...range(3, 4 - 1), 4], 5], 6]
[...[...[...range(3, 3), 4], 5], 6]
[...[...[...[...range(3, 3 - 1), 3], 4], 5], 6]
[...[...[...[...range(3, 2), 3], 4], 5], 6]
[...[...[...[...[], 3], 4], 5], 6] // base case
[...[...[...[3], 4], 5], 6]
[...[...[3, 4], 5], 6]
[...[3, 4, 5], 6]
[3, 4, 5, 6]
using plus instead of minus
It should be helpful for us to see that we can solve range using + and prepend instead of - and append -
// range : (number, number) -> number array
function range(start, end) {
if (start > end) // ✅ when start exceeds end, exit
return []
else
return [start, ...range(start + 1, end)] // ✅ plus and prepend
}
range(3,6)
[3, ...range(3 + 1, 6)]
[3, ...range(4, 6)]
[3, ...[4, ...range(4 + 1, 6)]]
[3, ...[4, ...range(5, 6)]]
[3, ...[4, ...[5, ...range(5 + 1, 6)]]]
[3, ...[4, ...[5, ...range(6, 6)]]]
[3, ...[4, ...[5, ...[6, ...range(6 + 1, 6)]]]]
[3, ...[4, ...[5, ...[6, ...range(7, 6)]]]]
[3, ...[4, ...[5, ...[6, ...[]]]]] // base case
[3, ...[4, ...[5, ...[6]]]]
[3, ...[4, ...[5, 6]]]
[3, ...[4, 5, 6]]
[3, 4, 5, 6]
demo
function rangeplus(start, end) {
if (start > end)
return []
else
return [start, ...rangeplus(start + 1, end)]
}
function rangeminus(start, end) {
if (end < start)
return []
else
return [...rangeminus(start, end - 1), end]
}
console.log(rangeplus(3,6))
// [3, 4, 5, 6]
console.log(rangeminus(3,6))
// [3, 4, 5, 6]
You cannot understand a recursive algorithm ( not your own ) by reading its lines only.. so, you need to apply a simple example to get the right result and understand where it's goes to, this is my example :
First call
start = 1
end = 0
=>start > start
Algorithm result : return []; ===> an empty array
First call
start = 1
end = 1
=> start = end
Algorithm result : return [start]; ===> array of ONE element (start value)
First call
start = 1
end = 5
=> start < end
in this case the algorithm will call itself const numbers = range(start , end - 1); by changing the end value and storing it in array called numbers
Second call :
numbers = []
range(1,5-1)
(don't forget, "end" (4) is still greater than "start" so it will go to the recall iteration )
Third call :
numbers []
range(1,4-1)
=>"end" is still greater (3)
Fourth call :
numbers []
range(1,3-1)
=>"end" is still greater (2)
Fifth call :
numbers []
range(1,2-1)
=>now, "end"(1) is equals to "start" (1),the next iteration will act differently
Sixth call :
start = end ==> this call will return start value (1) and push it to the constant number
so, now, the algorithm will continue the execution of not executed lines after the previous calls (I mean numbers push() ):
Result of sixth call = 1
it will push it to numbers, and return numbers (array of one element [1])
Result of fifth call = 2
push to numbers ==> numbers is [1,2]
etc. Until it reaches the first call.
Final Result : [1,2,3,4,5]
I hope that it can help.

Spinning the elements of an array clockwise in JS

I am supposed to rotate an array of integers clockwise in JS.
Here is my code for it:
function rotateArray(N, NArray)
{
//write your Logic here:
for(j=0;j<2;j++){
var temp=NArray[N-1];
for(i=0;i<N-1;i++){
NArray[i+1]=NArray[i];
}
NArray[0]=temp;
}
return NArray;
}
// INPUT [uncomment & modify if required]
var N = gets();
var NArray = new Array(N);
var temp = gets();
NArray = temp.split(' ').map(function(item) { return parseInt(item, 10);});
// OUTPUT [uncomment & modify if required]
console.log(rotateArray(N, NArray));
The code accepts an integer N which is the length of the array. The input is as follows:
4
1 2 3 4
The correct answer for this case is supposed to be
4 1 2 3
But my code returns
4 1 1 1
I cannot find where my code is going wrong. Please help me out.
All you need to do is move one item from the end of the array to the beginning. This is very simple to accomplish with .pop() (removes an item from the end of an array), then declare a new array with that element as the first:
function rotateArray(N, NArray) {
const lastItem = NArray.pop();
return [lastItem, ...NArray];
}
console.log(rotateArray(1, [1, 2, 3, 4]));
Doing anything else, like using nested loops, will make things more unnecessarily complicated (and buggy) than they need to be.
If you don't want to use spread syntax, you can use concat instead, to join the lastItem with the NArray:
function rotateArray(N, NArray) {
const lastItem = NArray.pop();
return [lastItem].concat(NArray);
}
console.log(rotateArray(1, [1, 2, 3, 4]));
If you aren't allowed to use .pop, then look up the last element of the array by accessing the array's [length - 1] property, and take all elements before the last element with .slice (which creates a sub portion of the array from two indicies - here, from indicies 0 to the next-to-last element):
function rotateArray(N, NArray) {
const lastItem = NArray[NArray.length - 1];
const firstItems = NArray.slice(0, NArray.length - 1);
return [lastItem].concat(firstItems);
}
console.log(rotateArray(1, [1, 2, 3, 4]));
function rotate(array,n){
Math.abs(n)>array.length?n=n%array.length:n;
if(n<0){
n=Math.abs(n)
return array.slice(n,array.length).concat(array.slice(0,n));
}else{
return array.slice(n-1,array.length).concat(array.slice(0,n-1));
}
}
console.log(rotate([1, 2, 3, 4, 5],-3));
The answer by #CertainPerformance is great but there's a simpler way to achieve this. Just combine pop with unshift.
let a = [1,2,3,4];
a?.length && a.unshift(a.pop());
console.log(a);
You need to check the length first so you don't end up with [undefined] if you start with an empty array.

Recursive function that creates an array from 0 to n (JavaScript)

I've been reading a lot about recursive functions recently though I wasn't able to figure this one out until today. I think I now understand recursion better. Hopefully I can help someone else who is still struggeling with it:
function count(n) {
if (n === 1) {
return [1];
} else {
var numbers = count(n - 1);
numbers.push(n);
return numbers;
}
}
console.log(count(3));
The result of count(3) will be: [1, 2, 3]
We pass in 3 into count(n). Since n is not equal to 1 we go straight to the else statement. numbers = count(3 - n) or numbers = count(2) if we pass in n. What happens next is recursion:
Since we don't know what exactly count(2) is we have to run it to figure out. So we run count(2). n is not equal to 1 so we go to the else statement again. numbers = count(1). Recursion again. We put in 1 for n and this time the function returns [1].
Now since we know that count(1) = [1] we are able to solve count(2). count(2) is numbers = count(1) or numbers = [1]. This time we go ahead in our code and push in n numbers.push(n) which is 2. So count(2) returns [1, 2]. Now since we know the result for count(2) let's solve count(3). count(3) is numbers = count(2) or numbers = [1, 2] putting in our result. Now push gets activated and voila, our result for count(3) is [1, 2, 3].

Making a function that puts a number in the middle of an array

I'm really new to JavaScript and I've been struggling with this code for a test to get into a coding bootcamp. I wonder if anyone had any help they could give?
function putInTheMiddle(array, item) {
for (i = 0; i < 20; i++) {
let Middle = Floor.Math(array / 2);
array.slice(Middle, 0, item);
}
console.log putInTheMiddle([1, 3], 2);
console.log putInTheMiddle([1, 2, 4, 5], 3);
}
I would like it to print out the arrays as [1, 2, 3] and [1, 2, 3, 4, 5] respectively.
You don't need the loop, that will insert the number 20 times.
The function to get the floor of a value is Math.floor(), not Floor.Math().
You need to divide the array's length by 2, not the array itself.
The function modifies the array in place, but if you want to be able to call console.log() when calling the function, it needs to return the array as well.
You need to use the splice() method to insert into the array. slice() just extracts part of the array without modifying.
You need to put parentheses around the argument to console.log(), and this needs to be outside the function definition.
function putInTheMiddle(array, item) {
let Middle = Math.floor(array.length / 2)
array.splice(Middle, 0, item);
return array;
}
console.log(putInTheMiddle([1, 3], 2));
console.log(putInTheMiddle([1, 2, 4, 5], 3));
Try Math.floor rather than Floor.Math:
const putInTheMiddle = (array, item) => {
let middleOfArray = Math.floor(array.length / 2);
array.splice(middleOfArray, 0, item);
return array;
}
console.log(putInTheMiddle([1, 3], 2));
console.log(putInTheMiddle([1, 2, 4, 5], 3));
Use Math.floor with array.length - also, console.log is a function, so call it with parentheses ():
function putInTheMiddle(array, item) {
array.splice(Math.floor(array.length / 2), 0, item);
return array;
}
console.log(putInTheMiddle([1, 3], 2));
console.log(putInTheMiddle([1, 2, 4, 5], 3));
.as-console-wrapper { max-height: 100% !important; top: auto; }
What the OP posted needed a few corrections which the following code reflects:
function putInTheMiddle(arr, item) {
let m = Math.floor((arr.length / 2));
arr.splice(m, 0, item);
return arr;
}
let arr = putInTheMiddle([1, 3], 2);
console.log(`[` + arr.join() + `]`);
let arr2 = putInTheMiddle([1, 2, 4, 5], 3);
console.log(`[` + arr2.join() + `]`);
The user-defined function needs to return the altered array in each case if console.log is to display the intended result. Note, Math is an object and floor is a method just as console is an object and log is a method. The methods require parentheses.
In order to determine the middle index of the array, you need to find its length, easily available since it is a property of the array. After dividing the length in two, you need to round the result to the nearest integer less than or equal to its value and Math.floor() will aid in this endeavor.
Instead of using the slice method, the code utilizes the Array object's splice method which fits the task at hand perfectly.
I concur with #Barmar that the OP does not need the for-loop.
Lastly, I use the Array object's join() method to join the elements into a string for easy display, which does not permanently affect the array.
Since, you are new to javascript i would like to answer this with simple methods.
//array is the array in which you want to insert.
//item is the value which you want to insert.
function putInMiddle(array, item){
let index = Math.floor(array.length/2);
//array.splice is a method which takes 3 arguments.
//argument 1 is the index where you want to make change.
//argument 2 is number of elements you want to remove starting from that index.
//argument 3 is the element that you want to insert at that index.
//In your case no element needs to be removed, hence argument 2 is 0.
array.splice(index,0,item);
return array;
}
//Then you can call the function in console.log if you just want to print the value.
console.log(putInMiddle(['1','2','4','5'],'3'));
//This will print ['1','2','3','4','5']
The above code works perfectly if you just want to add 1 element. But what if you want to add more than one element at a time. Relax, i have got you covered for that. Use the following:
function putInMiddle(array, itemArr){
let index = Math.floor(array.length/2);
let numItems = itemArr.length;
for(let i=0; i<numItems; i++){
array.splice(index,0,itemArr[i]);
index++;
}
return array;
}
console.log(putInMiddle(['1','2','6','7'],['3','4','5']));
//This will print ['1','2','3','4','5','6','7']
Hope this helps!

Categories

Resources