Weird bug in Javascript splice method - javascript

I have an array which contains "Zeros" and I want to move all of
the "Zeros" to the last indexes of the array.
The expected output is:
[1,2,3,0,0,0,0]
But instead I get:
[1,2,0,3,0,0,0]
let a = [0, 1, 2, 0, 0, 3, 0];
let count = 0;
let len = a.length;
for (i = 0; i < len; i++) {
if (a[i] == 0) {
count = count + 1;
a.splice(i, 1);
}
}
for (j = 0; j < count; j++) {
a.push(0);
}
console.log(a);

When you remove the item from the array all the element shift down by one. When you advance your index (i++), you skip the shifted down item in the array which happens to be successive zero in the array.
Solution: Do the for next loop backward and it'll work.

Because splice changes the length of the array, you could iterate from the end of the array and splice the found value directly to the last index.
With this approach, you need only a single loop.
var a = [0, 1, 2, 0, 0, 3, 0],
i = a.length;
while (i--) {
if (a[i] === 0) {
a.splice(a.length, 0, ...a.splice(i, 1));
}
}
console.log(a);
A shorter approach without splicing - and starting from zero.
var a = [0, 1, 2, 0, 0, 3, 0],
i, j = 0;
for (i = 0; i < a.length; i++) {
if (a[i] !== 0) {
[a[j], a[i]] = [a[i], a[j]]; // swap
j++;
}
}
console.log(a);

You can do it much simpler with Array.prototype.sort():
const array = [0, 1, 2, 0, 0, 3, 0];
const sortedArray = array.sort((a, b) => {
if (a === 0) {
return 1;
}
if (b === 0) {
return -1;
}
return a - b;
});
console.log(sortedArray);

In the for loop when you splice the array the array and it length are changed.
for that you must fix the i in the for loop by subtract 1
i++;
and fix the length by subtract 1 or reget the length again
let a = [0, 1, 2, 0, 0, 3, 0];
let count = 0;
let len = a.length;
for (i = 0; i < len; i++) {
if (a[i] == 0) {
count = count + 1;
a.splice(i, 1);
len = a.length;
i--;
}
}
for (j = 0; j < count; j++) {
a.push(0);
}
console.log(a);

Instead of splicing the Array over and over again, here is a different approach:
let a = [0, 1, 2, 0, 0, 3, 0];
// create some more (random) data
for (let i = a.length; i < 30; ++i)
a[i] = Math.floor(Math.random() * Math.random() * 10);
console.log(""+a);
let i = 0, j = 0, len = a.length;
// move non-0 values to the front
while (i < len) {
if (a[i] !== 0) {
a[j++] = a[i];
}
++i;
}
// fill the end of the list with 0
while (j < len) a[j++] = 0;
console.log(""+a);

You could add i--; and len--; each time you use splice:
let a = [0, 1, 2, 0, 0, 3, 0];
let count = 0;
let len = a.length;
for (i = 0; i < len; i++) {
if (a[i] == 0) {
count = count + 1;
a.splice(i, 1);
i--; len--;
}
}
for (j = 0; j < count; j++) {
a.push(0);
}
console.log(a);
This is because when you splice 1 element, the keys of the array are shifted down by one, so the key of the next element you want to check is the same as the one you just removed. The len is also corrected with len--; because we just removed an element.
While this answer is the correct way of doing this using your original plan, it is kind of a fix. Your problem was that you loop over an array, and that array loses elements during the loop, and generally the right approach in these situations is to loop backward. This way the elements that risks having their key changed during the loop are the elements we already checked.

Note that each call to splice has generally O(n) complexity. There are many ways to instead achieve your desired result an order of magnitude more efficiently with a single O(n) iteration. Here's one:
let a = [0, 1, 2, 0, 0, 3, 0]
for (let i=0, j=0; j<a.length; j++)
if (a[j] && i != j)
[a[i++], a[j]] = [a[j], 0]
console.log(a)

Related

Checking for duplicates within an array

I am trying to check for matching instances of arrays within a larger array. To do this, I am implementing a condition where if two of three numbers in an array match two of the three members of any of the arrays within a larger array, there is a continue statement to go back to a previous loop:
var threeSum = function (nums) {
let result = [];
for (let i = 0; i < nums.length - 2; i++) {
for (let j = i + 1; j < nums.length - 1; j++) {
loop1: for (let k = j + 1; k < nums.length; k++) {
if (nums[i] + nums[j] + nums[k] === 0) {
let smallArray = [nums[i], nums[j], nums[k]].sort((a, b) => a - b);
for (let l = 0; l < smallArray.length && result[l]; l++) {
if (
smallArray[0] == result[l][0] &&
smallArray[1] == result[l][2]
) {
console.log("we already have this array")
continue loop1;
}
}
result.push(smallArray);
}
}
}
}
return result;
};
Thus, for example threeSum([-1, 0, 1, 2, -1, -4]) should return [[-1, 0, 1], [-1, -1, 2]] when is instead is returning [[-1, 0, 1], [-1, -1, 2], [-1, 0, 1]]. I have checked using the console.log in the inner most conditional, and the if statement is never returning as true, so it is never entering the continue command. But the first and third arrays should meet the requirements of this, so it seems as if when checking the third array, the function should kick back the command.
I am a bit puzzled as to what is going wrong.
A single loop approach but with recursion and filtering for arrays with same values.
function threeSum(array, temp = [], sum = 0, i = 0) {
if (temp.length === 3) return sum ? [] : [temp];
const result = [];
while (i < array.length) {
result.push(...threeSum(array, [...temp, array[i]], sum + array[i], ++i));
}
return result.filter((s => a => (b => !s.has(b) && s.add(b))([...a].sort().join('|')))(new Set));
}
console.log(threeSum([-1, 0, 1, 2, -1, -4]));

2D Array loop behaving strangely (FCC)

I'm doing a learning exercise and am trying to understand the following code. I thought I had a handle on arrays and loops, but this one has got me very confused.
The below code:
function zeroArray(m, n)
{
let newArray = [];
let row = [];
for (let i = 0; i < m; i++)
{
for (let j = 0; j < n; j++)
{
row.push(0);
}
newArray.push(row);
}
return newArray;
}
let matrix = zeroArray(3, 2);
console.log(matrix);
Returns
[ [ 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0 ] ]
However I would have expected it to return
[ [ 0, 0, ],
[ 0, 0, 0, 0, ],
[ 0, 0, 0, 0, 0, 0 ] ]
Given that in each i loop, we are pushing (0) to row[] twice, before pushing row[] into newArray.
This isn't happening though, and in my VSCode debugger it looks as though in each i loop, every existing index of newArray is being updated with the latest version of the row[] array.
Why is this?
1) Start outer loop with i = 1 upto i <= m, so the loop count will be m
for (let i = 1; i <= m; i++) {
2) You should create a new row every time the inner loop start and push row into newArray after the inner loop ends
3) Set inner loop condition as j < n * i
for (let j = 0; j < n * i; j++) {
function zeroArray(m, n) {
let newArray = [];
// const row = [] // (-)
for (let i = 1; i <= m; i++) {
const row = []; // (+)
for (let j = 0; j < n * i; j++) { // (+)
row.push(0);
}
newArray.push(row);
}
return newArray;
}
let matrix = zeroArray(3, 2);
console.log(matrix);
/* This is not a part of answer. It is just to give the output fill height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
You need to make a copy of the array when pushing to newArray:
function zeroArray(m, n) {
let newArray = [];
let row = [];
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
row.push(0);
}
newArray.push(row.slice());
}
return newArray;
}
let matrix = zeroArray(3, 2);
console.log(JSON.stringify(matrix));
Matrix m x n should be m rows and n cols.
So for 3, 2 you expect
[
[0, 0],
[0, 0],
[0, 0],
]
Just declare row inside the first loop:
function zeroArray(m, n) {
const newArray = [];
for (let i = 0; i < m; i++) {
const row = [];
for (let j = 0; j < n; j++) {
row.push(0);
}
newArray.push(row);
}
return newArray;
}
let matrix = zeroArray(3, 2);
console.log(matrix);

Loop running wrong number of times

I am trying to remove certain values of a list doing a simple for loop. The loop seems to run 1 less time than expected. If I run children.length+1 it gives me an out of bounds error. Can someone see why this is occurring by simply looking at this code?
var children = document.getElementsByClassName("w-dyn-items")[0].children;
console.log(children.length) // 5
for (var i = 0; i < children.length; i++) {
var tableChild = children[i];
console.log(i, children[i]); // i = 0,1,2,3
id = tableChild.getElementsByClassName("text-block-3")[0];
console.log(id.textContent, msid)
if (id.textContent != msid) {
children[i].remove();
}
}
You're removing children, once you remove a child, children.length decreases., and when you check the next index, you will "skip" a row. You just need to decrement i when you remove:
if (id.textContent != msid){
children[i].remove();
i--;
}
E.g.
let arr = [0, 1, 2]
for (let i = 0; i < arr.length; i++) {
// i = 0; arr = [0, 1, 2]; arr[i] === 0;
// i = 1; arr = [1, 2]; arr[i] === 2; we skipped 1
if (arr[i] !== 2) arr[i].remove();
}

I need to count the number of consecutive zeroes and update the new array with such count. each such block of zeroes has to be counted separately

I need to count consecutive blocks which are filled with zero and update the array result with each block separately like this [3,2,5]
This is my attempt, but it gives me the total number of zeros [10].
Also i need to return the Position of first Element of each Block in another array like this [2,6,11].
var A = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0];
var N = A.length;
function AvailConseblocks(A) {
var counter = 0;
var result = [];
var POS = [];
for (let i = 0; i < A.length; i++) {
if (A[i] === 0) {
counter++;
POS.push(i)
}
}
result.push(counter);
return result
}
console.log(AvailConseblocks(A));
let arr = [1,0,0,0,1,1,0,0,1,1,1,0,0,0,0,0]
let result =arr.join('').split(/[^0]/g).filter(e => e).map(e => e.length)
console.log(result)
This should work, not tested for edge cases.
var A = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0];
var N = A.length;
function AvailConseblocks(A) {
var counter = 0;
var result = [];
for (let i = 0; i < A.length; i++) {
if (A[i] === 0) {
counter++;
} else {
// if element is not zero and counter is not zero as well
// means we found 0 or 0's surrounded by non-zero elements
// we push the counter and re-intialize it to zero
if (counter !== 0) {
result.push(counter);
counter = 0;
}
}
}
// this is important if last element of the array was zero as well
if (counter !== 0) {
result.push(counter);
}
return result;
}
console.log(AvailConseblocks(A));
It can do the job done
const countNumbers = (count) => {
let arr = [];
for (let i = 0; i < count.length; i++) {
arr = arr.concat(count[i]);
}
let zero = 0;
for (let i = 0; i < arr.length; i++) {
if (arr[i] === 0) zero++;
}
return zero;
};
console.log(countNumbers([2, 3, 44, 0, 0, 0, 00])); // ans : 4

JavaScript bubble sort modification (condition of the loop "for" is not working)

Loop "i" must break, when if statement of loop "j" return swap = false, but it doesn't do that, and proceeding through all of arr.length
bubble sort pen link
var arr = [0, 1, 2, 4, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13];
var n = arr.length;
var t;
var swap;
for (var i = 0; (i < n) && (swap = true); i++) {
for (var j = 0; j < ( n - (i + 1) ); j++) {
if ( arr[j] > arr[j + 1] ) {
t = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = t;
swap = true;
} else { swap = false }
}
};
this line
for (var i = 0; (i < n) && (swap = true); i++) {
assigns a value to swap (i.e. swap = true), whereas you actually want to check its value (.i.e. swap === true).
Note that swap is initially undefined, so if you change your code in line with the above (swap === true) you may want to explicitly set it to true before entering the loop.
You can use the break; statement to stop the for() loop
Have a look here
var arr = [0, 1, 2, 4, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13];
var n = arr.length;
var t;
var swap = true;
for (var i = 0; i < n; i++) {
if (!swap) {
break;
}
for (var j = 0; j < ( n - (i + 1) ); j++) {
if ( arr[j] > arr[j + 1] ) {
t = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = t;
} else {
swap = false;
}
}
};
alert(performance.now());
alert(arr);
correct answer by duncan smith
this line
for (var i = 0; (i < n) && (swap = true); i++) {
assigns a value to swap (i.e. swap = true), whereas you actually want to check its value (.i.e. swap === true).
Note that swap is initially undefined, so if you change your code in line with the above (swap === true) you may want to explicitly set it to true before entering the loop.

Categories

Resources