强制数组在排序后重新计算长度

时间:2012-09-13 03:21:02

标签: javascript google-chrome

如果你拿一个数组并执行以下操作:

arr = [];
arr[100] = 1;

长度为101,由于0-99被设置为undefined

,这是有道理的

现在,如果我们对该数组进行排序:arr.sort()它将如下所示:[1, undefined x100]因为不保留密钥。但是,长度仍然是101,因为undefined已全部移动到最后,而不是移除。

这种行为是否是故意的,如果是这样的话:是否有内置函数删除undefined并重新计算,为什么有意?

询问如何编写我自己的函数来重新计算长度。使用for (x = 0; arr[x] != undefined; x++);arr.length = x;

可以轻松强制排序数组的长度

3 个答案:

答案 0 :(得分:5)

> arr = [];
> arr[100] = 1;

> The length will be 101, which makes sense due to 0-99 being set as undefined

数组在100处只有一个成员,它就像:

var arr = { '100': 1, 'length': 101};
  

现在,如果我们对该数组进行排序:arr.sort()它将如下所示:[1,undefined x100]

不,它没有。它有一个成员为0,长度为100.没有100个成员的值为'undefined'。

如果您希望将数组简化为仅定义的成员,则没有内置函数来执行此操作(即基本上将 length 设置为具有值的最高索引)。

你只能通过迭代数组来做到这一点,例如通过减少从右边到现有属性的长度:

function trimArray(arr) {
  var i = arr.length;
  while ( !(--i in arr)) {
    arr.length -= 1;
  } 
}    

请注意,要使上述方法正常工作,必须对数组进行排序。此外,它还会修改传入的数组。

这是一种使用compress方法扩展Array.prototype的方法,因此只存在已定义成员的数组,并且适当地重置了长度:

if (!('compress' in Array.prototype)) {
  Array.prototype.compress = function() {

    var i = 0,
        lastExisting = 0,
        len = this.length;

    do {

      if (i in this) {
        this[lastExisting++] = this[i];
      }
    } while (++i < len)

    this.length = lastExisting; 
  }
}

请注意,它不会删除值 undefined 的成员,只会删除那些根本不存在的成员。所以:

var x = [,,,,1,,,2];
x[20] = void 0;

x.compress()

alert(x + '\n' + x.length); // [1,2,<undefined>], length = 3

修改

正如zzzzBov所指出的,这也可以使用filter

来完成
var x = x.filter(function(item){return true;});

将使用仅包含已定义成员的新数组替换x,无论其值如何。它将是非稀疏的(或连续的)并具有适当的长度,例如

var x = [,,,,1,,,2];
x[20] = void 0;  // set x[20] to undefined, length is 21

x = x.filter(function(item){return true;});  // [2, 3, <undefined>], length = 3

请注意,此方法仅更新x引用的数组,不会更新对同一数组的任何其他引用,例如

 var x = [,,1];
 var y = x;  // x and y reference same array

 x = x.filter(function(item){return true}); // x is now another array of members
                                            // filtered from the original array

 alert(x); // [1] x references a different array
 alert(y); // [,,1] y still references the original array

如果需要保留原始数组,这可能是有用的行为。相反,compress方法修改原始数组:

 var x = [,,1];
 var y = x;  // x and y reference same array

 x.compress();

 alert(x); // [1] x is a modified version of the original array
 alert(y); // [1] y references the same (original) array

我不知道是否有意在未来版本的ECMAScript中将这种方法添加到Array.prototype或者它可能被调用的内容,因此可能建议使用其他名称,例如xcompress或类似的,以避免与未来版本的意外冲突,并明确表明它是一种本机方法,而非内置方法。

我也有点难以找到合适的名字,因为压缩压缩已经定义了与非连续,但这看起来很笨重,所以现在压缩

答案 1 :(得分:1)

arr = [];
arr[100] = 1;

代码不会将0-99设置为undefined,它只将密钥100设置为1,将length属性设置为{ {1}}。

并且101不会更改数组的属性Array.sort

排序后,length有两个属性,arr01仍为length

enter image description here

答案 2 :(得分:0)

如果您想要解决问题,请在对数组进行排序后对其进行filter(或在排序之前对其进行过滤)。

var arr;
arr = [];
arr[100] = 1;
arr.sort();
arr = arr.filter(function (item) {
    return typeof item !== 'undefined';
});

实际发生的是你正在覆盖length属性,Array对象使用该属性来确定数组末尾的位置,数组是否具有那么多属性