Javascript 拼接方法中的奇怪错误

Weird bug in Javascript splice method

我有一个包含 "Zeros" 的数组,我想移动所有 "Zeros" 到数组的最后一个索引。

预期输出为:

[1,2,3,0,0,0,0]

但我得到的是:

[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);

当您从数组中删除项目时,所有元素都会向下移动一位。当您推进索引 (i++) 时,您会跳过数组中向下移动的项目,该项目恰好是数组中的连续零。

解决方法:向后执行for next循环,就可以了。

您可以在每次使用 splice 时添加 i--;len--;:

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);

这是因为当你拼接1个元素时,数组的key被下移了一位,所以你要检查的下一个元素的key和你刚刚移除的元素的key是一样的。 len 也用 len--; 更正,因为我们刚刚删除了一个元素。

虽然此答案是使用您的原始计划执行此操作的正确方法,但它是一种修复方法。您的问题是您遍历了一个数组,并且该数组在循环过程中丢失了元素,通常在这些情况下正确的方法是向后循环。这样,在循环期间可能会更改密钥的元素就是我们已经检查过的元素。

您可以使用 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);

因为splice会改变数组的长度,所以可以从数组的末尾开始迭代,将找到的值直接拼接到最后一个索引。

使用这种方法,您只需要一个循环。

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);

没有拼接的更短的方法 - 从零开始。

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);

在for循环中拼接数组时,数组和它的长度都会改变。

为此,您必须通过减去 1

来修复 for 循环中的 i
  i++;

并通过减去 1 或重新获取长度来固定长度

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);

请注意,每个 splice 的调用通常有 O(n) complexity。有许多方法可以通过单个 O(n) 迭代更有效地实现您想要的结果一个数量级。这是一个:

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)

与其一遍又一遍地拼接数组,不如换一种方法:

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);