Tests fail when concatenating nested array in JavaScript - javascript

Implemented a Radix Sort which sorts a list of numbers. Here is my code:
function getDigit(number, index, lengthOfLongestNumber) {
let numberString = number.toString();
numberString = numberString.padStart(lengthOfLongestNumber, '0');
return parseInt(numberString[index], 10) || 0;
}
function getLengthOfLongestNumber(numbers) {
// return Math.floor(Math.log10(Math.max(...numbers))) + 1;
let largestNumber = 0;
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] > largestNumber) {
largestNumber = numbers[i];
}
}
return largestNumber.toString().length;
}
function radixSort(numbers) {
const lengthOfLongestNumber = getLengthOfLongestNumber(numbers);
for (let i = lengthOfLongestNumber - 1; i >= 0; i--) {
const buckets = new Array(10).fill().map(() => []);
while (numbers.length) {
const number = numbers.shift();
buckets[getDigit(number, i, lengthOfLongestNumber)].push(number);
}
for (let j = 0; j < 10; j++) {
// numbers = numbers.concat(buckets[j]); ---> uncomment this line and comment out the following while loop
// numbers = [...numbers, ...buckets[j]]; ---> or uncomment this line and comment out the following while loop
while (buckets[j].length) {
numbers.push(buckets[j].shift());
}
}
}
return numbers;
}
Tests fail when I merge buckets array into numbers array using concat() in radixSort function (inside inner for loop). But it somehow passes if I use while loop instead.
You can see and run tests in CodeSandbox.
Why is this happening? What is the difference between them that causes tests fail?

For the returned array it doesn't matter which of those alternatives you use, but if the tests expect you to sort the given array, so that after the call the input array has been sorted itself, then indeed, the commented-out alternatives will not work. This is because those alternatives assign to numbers instead of mutating it.
Just check the difference between the alternatives when ignoring the returned array, but looking at the given array:
let arr = [521, 219, 100, 422, 889, 529, 491, 777, 641, 882, 229];
radixSort(arr);
console.log(arr);
The commented-out alternatives will log an empty array.
The correct version mutates numbers, and does not assign a (new) array to it.
To avoid the loop, you can do this mutation also like this:
numbers.push(...buckets[j]);
And you can even replace the for loop around that statement, with just this line:
numbers.push(...buckets.flat());

Related

how would you iterate through an array, incrementing a variable in the global scope, when the loop hits certain elements?

I have a variable set to a string of about 30 characters. I used .split() to turn the string into an array. Now the part I am struggling with is looping through the array I just made, incrementing a counter I have set to a variable, and the variable incrementing anything the array iterates past the last 5 numbers in the array?
Thanks in advance to anyone that can help!
I tried this inside my function...
let numsArr = [0, 1, 2, 3, ...... ,30]
let numOfInvalidElement = 0;
function() {
for(let i = 0; i < numsArr.length; i++) {
if (numsArr >= 20) {
return numofInvalidElement++
}
}
}
1st solution:
const numsArr = []
// create the numsArr array with a for loop.
for (let i=0; i<30; i++) numsArr.push(i + 1);
// us a for...of loop to iterate numsArr
let numOfInvalidElement = 0;
for(const num of numsArr) {
if (num >= 20) numOfInvalidElement++
}
// log the result
console.log(numOfInvalidElement)
// expected output: 11
some other solutions:
const numsArr = []
// create the numsArr array with a for loop.
for (let i=0; i<30; i++) numsArr.push(i + 1);
// use a for loop to iterate numsArr:
let result1 = 0;
for (let i=0; i<numsArr.length; i++) if (numsArr[i] >= 20) result1++;
console.log(`for loop result: ${result1}`);
// use .forEach method to itrate numsArr:
let result2 = 0;
numsArr.forEach(num => (num >= 20) && result2++)
console.log(`.forEach method result: ${result2}`)
// use .filter method to itrate numsArr:
const result3 = numsArr.filter(num => num >= 20).length;
console.log(`.filter method result: ${result3}`)
// using .filter method with {} to itrate numsArr:
// when using array methods like .filter, .map, .some, etc. with {}, you do need to return a value.
const result4 = numsArr.filter(num => {
return num >= 20
}).length;
console.log(`.filter method result: ${result4}`)
You need to keep a check to know when the loop is approaching the last 5 values in the array. And also when you return from a for loop inside a function it ends the function execution.
let numsArr = [0, 1, 2, 3, ...... ,30]
let numOfInvalidElement = 0;
let isLastFiveNumbers = numsArr.length
function() {
const numsArrLength = numsArr.length
for(let i = 0; i < numsArrLength; i++) {
if (numsArrLength - i <= 5) {
numOfInvalidElement++
}
}
}
Your function is terminating early because of of the return statement. As the docs note:
The return statement ends function execution and specifies a value to be returned to the function caller.
To your requirement:
incrementing a counter I have set to a variable, and the variable incrementing anything the array iterates past the last 5 numbers in the array?
Not entirely sure what this means, nor what your condition numsArr >= 20 aims to achieve since numsArr is an array. Sounds like you want to only look at the last 5 elements and to only do something if the value exceeds a value e.g. 20:
for(let i = 0; i < numsArr.length; i++) {
// Only check last 5 and increment is exceed threshold value
if (i >= numsArray.length - 5 && numArr[i] >= 20) {
numofInvalidElement++; // Note the removed return
}
}
If that's what you want, then a functional approach may be more concise:
numOfInvalidElements = numsArr.slice(-5).filter(x => x >= 20).length;
See documentation on slice and filter.

How to return the length of a contiguous subarray with specific criteria

I'm tasked with creating an array that takes another array and returns the number of contiguous subarrays for each index that fulfills the following conditions:
-The value at index i is the maximum value of the subarray
-The contiguous subarray must begin or end with i
I'm almost there but I'm missing the code to ensure that the function checks the array elements that aren't sat directly either side of i (see my code below). I think I might need some sort of recursive call that runs the if statement again while passing through the updated values of 'forward' and 'backward'. Or maybe I'm taking the wrong approach altogether.
Any ideas?
function countSubarrays(arr){
var arr = [3, 4, 1, 6, 2];
var output = [];
for (var i = 0; i < arr.length; i++){
var total = 1;
var forward = 1;
var backward = 1;
if (arr[i] >= arr[i+forward]) {
total++;
forward++;
// Some sort of recursive call here?
}
if (arr[i] >= arr[i-backward]){
total++;
backward++;
// Some sort of recursive call here?
}
output.push(total);
}
console.log(output);
}
countSubarrays();
You need to go backwards or ahead and count the items who are smaller or equal to the actual element.
function countSubarrays(array) {
var output = [];
for (let i = 0; i < array.length; i++) {
let value = array[i],
total = 1;
j = i;
while (j-- && value >= array[j]) total++;
j = i;
while (++j < array.length && value >= array[j]) total++;
output.push(total);
}
return output;
}
console.log(countSubarrays([3, 4, 1, 6, 2]));

Javascript - Arrays - for-loop to forEach

Given an array of integers where every value appears twice except one, find the single, non-repeating value.
Follow up: do so with O(1) space.
1) This is incorrect, the idea is to iterate through twice and compare if any value the first time around is not equal to a the 2nd go around. If not, push the non equal value into a new array and return that.
2) Is forEach pretty much the same as a for-loop?
How could this be rewritten with a forEach?
This is not giving me the output I'd like, which for this example,
should just return 4
CODE
const nonRepeat = arr => {
let newArray = [];
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length; j++) {
if (arr[i] !== arr[j]) {
newArray.push(arr[i])
}
}
}
return newArray
}
console.log(nonRepeat([2, 5, 3, 2, 1, 3, 4, 5, 1]));
Convert from for loop to forEach loop
for
for (let i = 0; i < arr.length; i++) { [code] }
forEach eliminates need to create the iterative variable ( for example, i) and always checks every element in the array
arr.forEach( [code] )
See Here for Additional Syntax Help

Javascript returning a sparse array from "push" operations despite the logged value having numbers

I'm working on my backtracking algorithim skills and I've run into a problem. The problem is to generate all permutations of an array of distinct integers i.e
permute([1,2,3]) => [[1,3,2], [1,2,3], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]
I have the written like so:
var permute = function(nums) {
var backtrack = function(nums, chosen, solutions) {
if (chosen.length === nums.length) {
// I'm not mutating nums.length so I don't get why this is pushing on empty arrays
console.log(chosen);
solutions.push(chosen);
} else {
for (let i = 0; i < nums.length; i++) {
if (!chosen.includes(nums[i])) {
chosen.push(nums[i]);
backtrack(nums, chosen, solutions);
chosen.pop();
}
}
}
}
var chosen = [];
var solutions = [];
backtrack(nums, chosen, solutions);
return solutions;
}
When I log out the chosen array variable on line 5, it has 4 values as I expect. However, I noticed that Javascript claims it has 4 values but a length property of zero. This means that when I run my function permute([1,2,3]), I get a result of [[], [], [], [], [], []] or nums.length factorial number of sparse arrays. I suspect that my loop is the problem and I'm not fully understanding all these array references I'm passing around but I'm not sure what else to do. Logging out chosen is what I expect. I appreciate any help or further readings.
This is not specific to the Chrome console environment. If I run this inside of a node repl I see the same behavior.
You mutate the same array chosen. For pushing a result, you could add a copy of the chosen array.
solutions.push(chosen.slice());
The part
for (let i = 0; i < nums.length; i++) {
if (!chosen.includes(nums[i])) {
chosen.push(nums[i]);
backtrack(nums, chosen, solutions);
chosen.pop();
}
}
iterates all elements of nums and checks if the value is already in chosen. If not, the value is pushed into the array chosen. Then the backtracking takes place and after this, the last value of chosen gets removed.
At the end, chosen is an empty array, which is the result of the pushing of the same array/object reference.
As result, you get the right amount of items (6), but always the same empty array.
var permute = function(nums) {
var backtrack = function(nums, chosen, solutions) {
if (chosen.length === nums.length) {
// I'm not mutating nums.length so I don't get why this is pushing on empty arrays
//console.log(chosen);
solutions.push(chosen.slice());
} else {
for (let i = 0; i < nums.length; i++) {
if (!chosen.includes(nums[i])) {
chosen.push(nums[i]);
backtrack(nums, chosen, solutions);
chosen.pop();
}
}
}
}
var chosen = [];
var solutions = [];
backtrack(nums, chosen, solutions);
return solutions;
}
console.log(permute([1, 2, 3]));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Javascript Array Test Behaves False When Is True

I am trying to make a multiplayer poker game in Node.js, and I've been having a lot of issues lately. This is a major one. This code is supposed to identify a Straight hand from an array. However, my code apparently isn't universal. I made 2 arrays as test cases and different results are produced when even just of the arrays is identified as a straight. Please help.
   
Here's the code: 
var arr = [9,1,2,11,8,12,10]; // first array
var arr2 = [9,1,8,4,5,3,2]; // second array
var straight = [];
// Removes duplicate elements in an array
/* example:
array = removeDuplicates(array)
*/
function removeDuplicates(arr){
let unique_array = []
for(let i = 0;i < arr.length; i++){
if(unique_array.indexOf(arr[i]) == -1){
unique_array.push(arr[i])
}
}
return unique_array
}
//Sorts the array
arr.sort(function(a,b){return b-a});
//Removes duplicates
arr = removeDuplicates(arr);
// Displays sorted and cleaned up array
console.log(arr)
/*Basic translation: loops through the array
and if the difference between the a term and
the term after it is 1, it will append it to the
array 'straight'. It will break if the difference
is greater than 1. Then it will remove those elements
from the original array and retry to append consecutive
elements in the 'straight' array.
*/
for (var i=1; i<arr.length+1; i++) {
if (arr[i-1] - arr[i] === 1) {
straight.push(arr[i-1],arr[i]); // error occurs at this line
} else if (arr[i-1] - arr[i] > 1){
break; }
if (straight.length === 2) {
arr.splice(arr.indexOf(straight[0]),1)
arr.splice(arr.indexOf(straight[1]),1)
straight = [];
for (var i=1; i<arr.length; i++) {
if (arr[i-1] - arr[i] === 1) {
straight.push(arr[i-1],arr[i]);
}
}
}
};
// There are duplicates and I don't know why sometimes
straight = removeDuplicates(straight)
console.log(straight);
This doesn't work for some reason. But it will work fine ONLY for the first array if you change
straight.push(arr[i-1],arr[i]);
to
straight.push(arr[i-1],arr[i],arr[i]);
It works ONLY for the second array if you switch the variable names:
var arr2 = [9,1,2,11,8,12,10]; // first array
var arr = [9,1,8,4,5,3,2]; // second array
and run the code without further changes, I don't know why it does this. I even went as far as logging the boolean
arr[i-1] - arr[i] === 1
to the console (in the loop, I mean), and it comes out true four times in a row (going through the first 5 indexes of the array), so I don't know why it stops at 11 for the first array and decides 11-10 isn't 1.
your logic is a bit hard to follow - I think the issue you're seeing is due to clearing the straight array in the if (straight.length === 2) part. Here's my shot at simplifying things:
const isStraight = a => {
const uniq = a.filter((val, idx) => a.indexOf(val) === idx);
uniq.sort((a, b) => a-b);
const tries = uniq.length - 4;
for (var i=0; i<tries; i++) {
if (uniq[i + 4] - uniq[i] === 4) {
return true;
}
}
return false;
}
console.log(isStraight([9,1,2,11,8,12,10]));
console.log(isStraight([9,1,8,4,5,3,2]));
console.log(isStraight([2,5,4,3,6,8,7]));
console.log(isStraight([2,5,4,3,6,8,7,10]));
console.log(isStraight([2,5,2,4,7,3,6,8,8,8,8,7]));
console.log(isStraight([1,2,3,4,6,7,8,9,11,12,13,13]))
let arr = [9,1,2,11,8,12,10];
function checkStraight(arr) {
let answer = [];
if(arr.length < 5)
return false;
arr = [...new Set(arr)];
arr.sort(function(a,b){return b-a});
for(let index=0; index < arr.length; index++){
if(answer.length === 5) break;
if(answer.length === 0){
answer.push(arr[index])
}
if(answer[answer.length-1] - arr[index] === 1){
answer.push(arr[index]);
}else{
answer = [];
answer.push(arr[index])
}
}
return answer
}
console.log(checkStraight(arr));
You can try to run thru the code, should be quite simple. Basically instead of comparing between elements inside own array, we compare between two array, and conditionally push the matched straight card into new array
**assumptions: ** Since we are playing poker, assuming once 5 consecutive card been found, it's consider a straight and no further checking needed

Categories