I don't understand this case of recursion - javascript

This is a solution from this exercice on FreeCodeCamp
function countdown(n) {
if (n < 1) {
return [];
} else {
const arr = countdown(n - 1);
arr.unshift(n);
return arr;
}
}
I understand the concept of recursion. I get that the function countdown will be repeated until n < 1 and the following command arr.unshift(n) will be executed once all possible values of n will be evaluated. What bugs me is that I don't get when or how const arr becomes and array.

What bugs me is that I don't get when or how const arr becomes and
array.
See the base case returns an empty array? This is where it all starts, thanks to that you are able to proceed with the array structure once the code returns from the recursive calls.
Code explanation
Unshift will append at zeroth index the LIFO, meaning
the last which was in is 1 and it will be added to the array, after that the array will add number 2 at zeroth index and at second index you'll have number 1, after that number 3 will be added at zeroth index, second index will be number 2, and last index will contain number 1. Now you see the pattern...
Try to console.log in between
function countDown(n) {
if (n < 1) {
return [];
} else {
const countArray = countDown(n - 1);
console.log(n)
countArray.unshift(n);
return countArray;
}
}
console.log(countDown(5));
If you are more used to ES6+ syntax, add the first element n at the first position of the array and de-structure the rest.
function countDown(n) {
if (n < 1) {
return [];
} else {
const countArray=[n,...countDown(n - 1)];
return countArray;
}
}
console.log(countDown(5));
And at last, you can make it a one liner with ternary operators:
const countDown = (n) => n < 1 ? [] : [n, ...countDown(n - 1)];
console.log(countDown(5));

Let's follow the pattern for a relatively simple call. Here again is the function:
function countdown(n) {
if (n < 1) {
return [];
} else {
const arr = countdown(n - 1);
arr.unshift(n);
return arr;
}
}
Say you call countdown(2):
Loop 1:
1a) n is not less than 1
1b) an arr is initialized and set to countdown(1)
Loop 2:
2a) n is not less than 1
2b) an arr is initialized and set to countdown(0)
Loop 3:
3a) n is less than 1
3b) an empty array is returned to loop 2
arr is []
Loop 2 cont.
2c) arr.unshift adds the number 2 to the start of the array.
arr is [2]
2d) arr is returned to loop 1
Loop 1 cont.
1c) arr.unshift adds the number 1 to the start of the array.
array is [1,2]
1d) arr is returned to function call
So if you wrote let result = countdown(2) and console.log(result), you will get: [1,2]
What bugs me is that I don't get when or how const arr becomes and
array.
Look at steps 3b, 2b, and 2c:
3b) an empty array is returned to loop 2
2b) an arr is initialized and set to countdown(0)
2c) arr.unshift adds the number 2 to the start of the array.
An empty array is set to const arr, then the number 2 is added to that array.
Now look at steps 2d and 1c:
2d) arr is returned to loop 1
1c) arr.unshift adds the number 1 to the start of the array.
That array is set to a new array like this (const arr = [2]) and the number 1 is added to that array. Finally in 1d, that new array is returned to the expression that called the function. For every recursive loop you have something like this:
const arr = something
new const arr = old const arr

Related

How to use reduce with lambda to put each word in a js array of arrays

Output should look like this:[[],[],[],[],[4 letter words], [5 letter words], [6 letter words], ... [20 letter words]. I created an array with 21 empty subarrays and I'm trying to push the words in from a different array called wordArray.
let acc = []
let i = 0
while ( i < 21){ // creating array with 21 empty subarrays
acc.push([])
i++
}
let reducer = function(acc, key) {
return acc = wordArray.reduce(function(acc, word) {
(acc[word[key]] = acc[word[key]] || [] ).push(word)
//acc.push(word)
return acc
}, [])
};
console.log(reducer(acc, 'length'));
My code pushes the words into arrays depending on the word length but it needs to also add empty subarrays depending word.length starting at zero and going through the 21st slot of the acc array. For example there are no words being stored that are less than 4 letters long, so the array should hold 4 empty arrays [] as well as empty arrays for words up to length 21.
I think this would be a lot easier to approach by removing the confusion of reduce, the code is a lot clearer if you just iterate over the words and push/assign to indicies of acc in a loop:
const wordArray = ['foobar'];
let acc = []
let i = 0
while (i < 21) {
acc.push([])
i++
}
let reducer = function(acc, key) {
for (const word of wordArray) {
(acc[word[key]] = acc[word[key]] || []).push(word)
}
return acc;
};
console.log(reducer(acc, 'length'));
If you really wanted to use reduce, make sure to pass in the acc you constructed into it as the initial value for the accumulator (right now you're passing in an initial accumulator of [], not the multidimensional array)
const wordArray = ['foobar'];
let acc = []
let i = 0
while (i < 21) { // creating array with 21 empty subarrays
acc.push([])
i++
}
let reducer = function(acc, key) {
return acc = wordArray.reduce(function(acc, word) {
(acc[word[key]] = acc[word[key]] || []).push(word)
//acc.push(word)
return acc
}, acc)
};
console.log(reducer(acc, 'length'));

javascript function to find the second largest element in an array

I am completing the hackerrank's 10 days of javascript challenge. The question:
write a function to take an array as an argument and then return the second largest element in the array.
I have written the code but my code is returning the largest element and not the second largest as asked.
function getSecondLargest(nums) {
// Complete the function
var largest=nums[0];
for(let i=1;i<nums.length;++i)
{
if(nums[i]>largest)
largest=nums[i];
}
var large=nums[0];
for(let j=1;j<nums.length;++j)
{
if(large<nums[j]&&large<largest)
large=nums[j];
}
return large;
}
When input array nums={2,3,6,6,5} the result is coming 6 while expected output is 5. Please help and point out the errors in the function code below.
should not initialize large with first value var large=nums[0]; because it may appear the biggest value and won't work
should use nums[j]<largest instead of large<largest as mentioned above
I think don't need second loop as all checks can be done in first loop, and you can assign prev largest to large whenever you change it:
function getSecondLargest(nums) {
var largest = nums[0];
var large;
for (let i = 1; i < nums.length; ++i) {
if (nums[i] > largest) {
large = largest;
largest = nums[i];
} else if (nums[i] > large || typeof large === 'undefined') {
large = nums[i]
}
}
return large;
}
console.log(getSecondLargest([5,1-2,3]))
console.log(getSecondLargest([-5,1,-2,3]))
GET SECOND LARGEST
first, I create new array with unique values.
let arr = [...new Set(nums)];
second, sort value using built-in function .sort().
note : by default .sort() always sorts asciibetically, but for some testcase, it doesn't work. So, I put (a, b) => { return a - b } to make sure it will work properly.
arr = arr.sort((a, b) => { return a -b });
third, get the value from arr
let result = arr[arr.length - 2] || arr[0];
finally, return the result
return result
function getSecondLargest(nums) {
let arr = [...new Set(nums)];
//Javascript's array member method .sort( always sorts asciibetically.
arr = arr.sort((a, b) => { return a - b });
let result = arr[arr.length - 2] || arr[0];
return result
}
Just one minor change:
Use nums[j]<largest instead of large<largest in the second for loop
function getSecondLargest(nums) {
// Complete the function
var largest=nums[0];
for(let i=1;i<nums.length;++i)
{
if(nums[i]>largest)
largest=nums[i];
}
var large;
//To ensure that the selected number is not the largest
for(let j=0;j<nums.length;++j)
{
if (nums[j] !== largest){
large = nums[j];
break;
}
}
for(let j=1;j<nums.length;++j)
{
if(large<nums[j]&&nums[j]!=largest)
large=nums[j];
else
console.log(large)
}
return large;
}
var secondLargest = getSecondLargest([6,3,6,6,5]);
console.log("Second largest number", secondLargest);
If you want to avoid using library functions like #ifaruki suggests, this line
if(large<nums[j]&&large<largest)
should read
if (large<nums[j] && nums[j] < largest)
Sorting and picking the second or second-to-last value fails when there are duplicates of the highest value in the input array.
Another easiest logic is to remove duplicates from the array and sort.
let givenArray = [2, 3, 6, 6, 5];
let uniqueArray = [...new Set(givenArray)];
console.log("The second largets element is", uniqueArray.sort()[uniqueArray.length - 2]);
I know you had your question answered, just thought I would provide my solution for any future users looking into this.
You can use reduce to go through the array while remembering the two largest numbers so far.
You just make a simple reduction function:
function twoMax(two_max, candidate)
{
if (candidate > two_max[0]) return [candidate,two_max[0]];
else if (candidate > two_max[1]) return [two_max[0],candidate];
else return two_max;
}
And then you use it for example like this:
let my_array = [0,1,5,7,0,8,12];
let two_largest = my_array.reduce(twoMax,[-Infinity,-Infinity]);
let second_largest = two_largest[1];
This solution doesn't require sorting and goes through the array only once.
If you want to avoid using **sort method. I think here's the easiest logic to do that, which will also work in arrays where there's duplicates of largest integer exists.
function getSecondLargest(arr) {
const largest = Math.max.apply(null, arr);
for (let i = 0; i < arr.length; i++) {
if (largest === arr[i]) {
arr[i] = -Infinity;
}
}
return Math.max.apply(null, arr);
}
console.log(getSecondLargest([5, 7, 11, 11, 11])); //7

Use Recursion to Create a CountdownPassed (JS Algorithm)

Question
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.
function countdown(n, newArr = []){
if(n == 1){
return newArr;
}
newArr.push(n);
return countdown(n - 1)
}
console.log(countdown(5));
My Question
Is there a way to fix this code so that it works?
I can provide an alternative solution, but I do not understand it:
function countdown(n) {
if (n < 1) {
return [];
} else {
const arr = countdown(n - 1);
arr.unshift(n);
return arr;
}
}
The problem is that you do not pass on the array to the recursive call, so each recursive execution creates a new, empty array. As a consequence, it does not return the array that had a value pushed to it, but the new, empty one that is coming back from the recursive calls.
Secondly, you never push value 1 to the array. So it would be better to stop the recursion at 0 instead of 1.
So taking those two fixes, you get this:
function countdown(n, newArr=[]) {
if (n <= 0) {
return newArr;
}
newArr.push(n);
return countdown(n - 1, newArr)
}
console.log(countdown(5));
Your alternative solution is clean, because it does not need to pass an array as argument. It uses the returned array to add the next value to it (in front of it). It would have my preference.
To understand how it works, print out the intermediate values:
function countdown(n) {
if (n < 1) {
console.log("At the end of recursion. Creating and returning an empty array.");
return [];
} else {
const arr = countdown(n - 1);
console.log("Got the following array back from the recursive call:");
console.log(JSON.stringify(arr));
arr.unshift(n);
console.log("Prefixing it with " + n + " and returning the result:");
console.log(JSON.stringify(arr));
return arr;
}
}
var result = countdown(5);
yes, you can modify your solution like that
function countdown(n){
if(n == 0){
// stop the function at 0 so it will not be included in the array
return [];
}
// concat the value of n as an array with the value less than it
return [n].concat(countdown(n - 1))
}
console.log(countdown(5));
the problem in your solution is that your array initialized as an empty array every time so the final answer will be an empty array
You need to hand over the result array for the recursive call. And you need to check if no value is left, ten return the result array.
function countdown(n, result = []) {
if (n < 1) return result;
result.push(n);
return countdown(n - 1, result);
}
console.log(countdown(5));
As another approach, you could return an array and for the exit condition take the final value, otherwise take n and the spreaded result of the recursive call.
function countdown(n) {
if (n < 1) return [];
return [n, ...countdown(n - 1)];
}
console.log(countdown(5));
At this point we will create the countdown function which call itself and called recursion.
function countdown(n) {
if (n < 1) {
return [];
} else {
console.log(n, "before calling");
const arr = countdown(n - 1);
console.log(n, "after calling");
return arr;
}
}
console.log(countdown(5));
And now when we know that the "before calling" is place where n is decrease and the "after calling" is place where n is increase, based on that we can do this.
const arr = [];
function countdown(n) {
if (n < 1) {
return arr;
} else {
arr.push(n);
return countdown(n - 1);;
}
}
console.log(countdown(5));

Scramble String According to Array Values - Javascript

Trying to solve this question on Codewars.
I've seen other articles that deal with shuffling / scrambling a string randomly.
But what about scrambling a string according to the values in a given array?
I.e. abcd given the array [0, 3, 2, 1] will become acdb because:
a moves to index 0
b moves to index 3
c moves to index 2
d moves to index 1
My guess is to start out by splitting the string into an array. And then we want to get the index value of the array that's passed into the scramble function, and push the character at the index value from that array into the new array. And finally join the array:
function scramble(str, arr) {
let newArray = str.split("");
let finalArray = [];
for (let i = 0; i < str.length; i++) {
console.log(newArray);
finalArray.push(newArray.splice(arr[i], 1));
}
return finalArray;
}
console.log(scramble("abcd", [0, 3, 1, 2]));
But the problem with this logic is that .splice() removes the character from the newArray every time.
Is there another method that will remove the character at the specified index without modifying the original array?
I don't think slice will work either.
You can make a separate array to store the letters.
var str = "abcd";
var arr = [0, 3, 2, 1];
function scramble(s, a) {
var ar = Array(a.length);
a.forEach((e, i) => {
ar[e] = s[i];
});
return ar.join('');
}
console.log(scramble(str, arr));
Answer
Use reduce on the array and as the array is iterated assign the character of the iterator index(i) in the string(s) to the value index(v) of the new array(ra). After the reduce completes, use join to turn the returned array(ra) back into a string.
let scramble = (s, a) => a.reduce((ra,v,i)=> (ra[v] = s[i], ra), []).join("");
Example:
let scramble = (s, a) => a.reduce((ra,v,i)=> (ra[v] = s[i], ra), []).join("");
console.log( scramble("abcd", [0,3,2,1]) );
Clarification Code:
I realize the above code may be hard to wrap your head around. Let me provide you with the same exact functionality, but in a standard function. Keep in mind this is exactly what is happening in the above code, but it may be simpler to comprehend if you're not used to the concision of ES6:
function scramble(my_string, my_array) {
// create an array to return
let returnable_array = [];
// loop through the provided array.
// string index is the array key: 0,1,2,3
// array_index is the value of the array keys: 0,3,2,1
for(let [string_index, array_index] of my_array.entries()) {
// we assign the character at string index
// to the value index inside the returnable array
returnable_array[array_index] = my_string[string_index];
}
// we turn the array into a string
let returnable_string = returnable_array.join("");
// we return the string
return returnable_string
}
Example:
function scramble(my_string, my_array) {
let returnable_array = [];
for(let [string_index, array_index] of my_array.entries()) {
returnable_array[array_index] = my_string[string_index];
}
returnable_string = returnable_array.join("");
return returnable_string
}
console.log(scramble("abcd", [0,3,1,2]));
You can loop over the input string get the character at the current position using string.charAt(position) and put it into a new array into the position retrieved from the positions array.
function scramble (str, arr) {
let newArray = [];
for (let i = 0; i < str.length; i++) {
newArray[arr[i]]=str.charAt(i);
}
return newArray.join();
}
console.log(scramble("abcd", [0, 3, 1, 2]));
I think the best approach would be to put the string into a new array:
function scramble(str, arr) {
//validate if the array has elements
if (arr && arr.length) {
//create a new array
const strArr = []
arr.forEach(index => {
//push each character by index
//As noted by Barmar you could either use
//str.charAt(index) or str[index]
//as both will return the character at the specified index
strArr.push(str.charAt(index))
})
//return a new string
return strArr.join('');
}
}

Return index of greatest value in an array

I have this:
var arr = [0, 21, 22, 7];
What's the best way to return the index of the highest value into another variable?
This is probably the best way, since it’s reliable and works on old browsers:
function indexOfMax(arr) {
if (arr.length === 0) {
return -1;
}
var max = arr[0];
var maxIndex = 0;
for (var i = 1; i < arr.length; i++) {
if (arr[i] > max) {
maxIndex = i;
max = arr[i];
}
}
return maxIndex;
}
There’s also this one-liner:
let i = arr.indexOf(Math.max(...arr));
It performs twice as many comparisons as necessary and will throw a RangeError on large arrays, though. I’d stick to the function.
In one line and probably faster then arr.indexOf(Math.max.apply(Math, arr)):
var a = [0, 21, 22, 7];
var indexOfMaxValue = a.reduce((iMax, x, i, arr) => x > arr[iMax] ? i : iMax, 0);
document.write("indexOfMaxValue = " + indexOfMaxValue); // prints "indexOfMaxValue = 2"
Where:
iMax - the best index so far (the index of the max element so far, on the first iteration iMax = 0 because the second argument to reduce() is 0, we can't omit the second argument to reduce() in our case)
x - the currently tested element from the array
i - the currently tested index
arr - our array ([0, 21, 22, 7])
About the reduce() method (from "JavaScript: The Definitive Guide" by David Flanagan):
reduce() takes two arguments. The first is the function that performs the reduction operation. The task of this reduction function is to somehow combine or reduce two values into a single value, and to return that reduced value.
Functions used with reduce() are different than the functions used with forEach() and map(). The familiar value, index, and array values are passed as the second, third, and fourth arguments. The first argument is the accumulated result of the reduction so far. On the first call to the function, this first argument is the initial value you passed as the
second argument to reduce(). On subsequent calls, it is the value returned by the previous invocation of the function.
When you invoke reduce() with no initial value, it uses the first element of the array as the initial value. This means that the first call to the reduction function will have the first and second array elements as its
first and second arguments.
Another solution of max using reduce:
[1,2,5,0,4].reduce((a,b,i) => a[0] < b ? [b,i] : a, [Number.MIN_VALUE,-1])
//[5,2]
This returns [5e-324, -1] if the array is empty. If you want just the index, put [1] after.
Min via (Change to > and MAX_VALUE):
[1,2,5,0,4].reduce((a,b,i) => a[0] > b ? [b,i] : a, [Number.MAX_VALUE,-1])
//[0, 3]
To complete the work of #VFDan, I benchmarked the 3 methods: the accepted one (custom loop), reduce, and find(max(arr)) on an array of 10000 floats.
Results on chromimum 85 linux (higher is better):
custom loop: 100%
reduce: 94.36%
indexOf(max): 70%
Results on firefox 80 linux (higher is better):
custom loop: 100%
reduce: 96.39%
indexOf(max): 31.16%
Conclusion:
If you need your code to run fast, don't use indexOf(max).
reduce is ok but use the custom loop if you need the best performances.
You can run this benchmark on other browser using this link:
https://jsben.ch/wkd4c
If you are utilizing underscore, you can use this nice short one-liner:
_.indexOf(arr, _.max(arr))
It will first find the value of the largest item in the array, in this case 22. Then it will return the index of where 22 is within the array, in this case 2.
Unless I'm mistaken, I'd say it's to write your own function.
function findIndexOfGreatest(array) {
var greatest;
var indexOfGreatest;
for (var i = 0; i < array.length; i++) {
if (!greatest || array[i] > greatest) {
greatest = array[i];
indexOfGreatest = i;
}
}
return indexOfGreatest;
}
var arr=[0,6,7,7,7];
var largest=[0];
//find the largest num;
for(var i=0;i<arr.length;i++){
var comp=(arr[i]-largest[0])>0;
if(comp){
largest =[];
largest.push(arr[i]);
}
}
alert(largest )//7
//find the index of 'arr'
var arrIndex=[];
for(var i=0;i<arr.length;i++){
var comp=arr[i]-largest[0]==0;
if(comp){
arrIndex.push(i);
}
}
alert(arrIndex);//[2,3,4]
EDIT: Years ago I gave an answer to this that was gross, too specific, and too complicated. So I'm editing it. I favor the functional answers above for their neat factor but not their readability; but if I were more familiar with javascript then I might like them for that, too.
Pseudo code:
Track index that contains largest value. Assume index 0 is largest initially. Compare against current index. Update index with largest value if necessary.
Code:
var mountains = [3, 1, 5, 9, 4];
function largestIndex(array){
var counter = 1;
var max = 0;
for(counter; counter < array.length; counter++){
if(array[max] < array[counter]){
max = counter;
}
}
return max;
}
console.log("index with largest value is: " +largestIndex(mountains));
// index with largest value is: 3
function findIndicesOf(haystack, needle)
{
var indices = [];
var j = 0;
for (var i = 0; i < haystack.length; ++i) {
if (haystack[i] == needle)
indices[j++] = i;
}
return indices;
}
pass array to haystack and Math.max(...array) to needle. This will give all max elements of the array, and it is more extensible (for example, you also need to find min values)
If you create a copy of the array and sort it descending, the first element of the copy will be the largest. Than you can find its index in the original array.
var sorted = [...arr].sort((a,b) => b - a)
arr.indexOf(sorted[0])
Time complexity is O(n) for the copy, O(n*log(n)) for sorting and O(n) for the indexOf.
If you need to do it faster, Ry's answer is O(n).
A minor modification revised from the "reduce" version of #traxium 's solution taking the empty array into consideration:
function indexOfMaxElement(array) {
return array.reduce((iMax, x, i, arr) =>
arr[iMax] === undefined ? i :
x > arr[iMax] ? i : iMax
, -1 // return -1 if empty
);
}
A stable version of this function looks like this:
// not defined for empty array
function max_index(elements) {
var i = 1;
var mi = 0;
while (i < elements.length) {
if (!(elements[i] < elements[mi]))
mi = i;
i += 1;
}
return mi;
}
To find the index of the greatest value in an array, copy the original array into the new array and then sort the original array in decreasing order to get the output [22, 21, 7, 0]; now find the value 22 index in the copyNumbers array using this code copyNumbers.indexOf(numbers[0]);
<script>
const numbers = [0, 21, 22, 7];
const copyNumbers = [];
copyNumbers.push(...numbers);
numbers.sort(function(a, b){
return b - a
});
const index = copyNumbers.indexOf(numbers[0]);
console.log(index);
</script>
Make this
const max = arr.reduce((m, n) => Math.max(m, n)), then the indexes of the max
get index with findIndex
var index = arr.findIndex(i => i === max)

Categories