【问题标题】:How to resize an array如何调整数组大小
【发布时间】:2015-08-17 15:27:51
【问题描述】:

有没有Array方法可以代替下面的函数(类似a.splice(some, tricky, arguments))?

function resize(arr, newSize, defaultValue) {
  if (newSize > arr.length)
    while(newSize > arr.length)
      arr.push(defaultValue);
  else
    arr.length = newSize;
}

如果没有,是否有更好/更好/更短/的实现?如果数组应该增长并在收缩时删除值,该函数应该附加一个默认值。

【问题讨论】:

  • arr.length = newSize; 会被undefined填满
  • 抛开效率不谈,您的while 循环会将太多值推送到数组中。应该是while (newSize > arr.length)
  • 如果你想提高效率,你不会在每次推送/弹出时增加/缩小数组。最好有一个足够大的数组并在必要时缩小或仅在意识到数组不够大时才增长
  • 嗯,有Array.prototype.fill,但尚未得到广泛支持。
  • 你能解释一下为什么你首先需要这种行为吗?

标签: javascript arrays


【解决方案1】:

就优雅而言,我想说您可以将原来的解决方案缩减为:

function resize(arr, newSize, defaultValue) {
    while(newSize > arr.length)
        arr.push(defaultValue);
    arr.length = newSize;
}

或者使用原型:

Array.prototype.resize = function(newSize, defaultValue) {
    while(newSize > this.length)
        this.push(defaultValue);
    this.length = newSize;
}

编辑:ES2016 方法:

function resize(arr, newSize, defaultValue) {
    return [ ...arr, ...Array(Math.max(newSize - arr.length, 0)).fill(defaultValue)];
}

【讨论】:

  • 这消除了比较但确保了分配。因此,这是否更有效取决于比较是否比分配更昂贵。
  • 注意:如果defaultValue 是一个对象,则此答案可能会导致不良行为。看my answer
  • 我不同意这个答案。每次推送到数组都会调整数组的大小。所以你应该首先设置长度,然后通过迭代其余项目来设置默认值。
  • 当您已经在第一个示例中更改长度时,重置 arr.length 有什么意义?这是多余的。
【解决方案2】:
function resize(arr, size, defval) {
    while (arr.length > size) { arr.pop(); }
    while (arr.length < size) { arr.push(defval); }
}

我认为这样会更有效率:

function resize(arr, size, defval) {
    var delta = arr.length - size;

    while (delta-- > 0) { arr.pop(); }
    while (delta++ < 0) { arr.push(defval); }
}

虽然没有那么优雅,但这可能是我最有效率的:

function resize(arr, size, defval) {
    var delta = arr.length - size;

    if (delta > 0) {
        arr.length = size;
    }
    else {
        while (delta++ < 0) { arr.push(defval); }
    }
}

【讨论】:

  • 最初将while 读取为if,这是我的错。所以这会起作用,但它的效率低于 OP 已经提出的。简单地将长度设置为较小的值对于限制超出特定大小的任何元素是有效的。
  • 这两种方法实际上是等价的。他们做同样的事情。 arr.length = size 比手动弹出每个元素要快,直到达到所需的大小。对于添加新元素,您在这两个函数中的代码等同于 OP 编写的代码。你只是表达方式不同。
  • @crush 该代码在添加项目方面效率更高一些,因为它不会在每次迭代时重新评估 arr.length
  • 右 arr.length = 会更好,添加另一个...谢谢
  • @Thriggle 这不是真的。他们是virtually equivalent。也许在某些较旧的浏览器中,这种说法是正确的。当您删除该虚假声明时,此答案的第三种方法正是 OP 提供的。
【解决方案3】:

扩展 James 解决方案:

Array.prototype.resize = function(newSize, defaultValue) {
    while(newSize > this.length)
        this.push(defaultValue);
    this.length = newSize;
}

如果您想提高效率,您可以对 Array.prototype.fill 进行浏览器检测,并使用它来代替 while 循环。

if (Array.prototype.fill) {
    Array.prototype.resize = function (size, defaultValue) {
        var len = this.length;

        this.length = size;

        if (this.length - len > 0)
            this.fill(defaultValue, len);
    };
} else {
    Array.prototype.resize = function (size, defaultValue) {
        while (size > this.length)
            this.push(defaultValue);

        this.length = size;
    };
}

如果有人为 Array.prototype.fill 添加了 polyfill,那么您希望他们使用您的非填充版本。 polyfill 会导致填充方法比非填充版本慢。

这个StackOverflow Q&A 处理如何检测一个函数是否是本机实现的。你可以在初始状态下使用它,但这只是额外的速度损失。

如果您能确保不存在Array.prototype.fill,我可能只会使用此解决方案。

【讨论】:

    【解决方案4】:

    对于单线爱好者

    如果你和我一样是单行情人, 你要问的是我的resize_array_right 做什么。


    const resize_array_left = (array, length, fill_with) => (new Array(length)).fill(fill_with).concat(array).slice(-length);
    // Pads left when expanding an array.
    // Put left elements first to be removed when shrinking an array.
    
    const resize_array_right = (array, length, fill_with) => array.concat((new Array(length)).fill(fill_with)).slice(0, length);
    // Pads right when expanding an array.
    // Put right elements first to be removed when shrinking an array.
    

    您可以在 NPM 中找到它,如resize-array


    浏览器兼容性

    • 铬:45 及以上。
    • Firefox:31 及以上。
    • Internet Explorer:✘。
    • 边缘:✔。

    【讨论】:

      【解决方案5】:

      您可以使用以下代码调整数组大小:

      // resizing function for arrays
      Array.prototype.resize = function( newSize, defaultValue )
      {
        while( newSize > this.length )
        {
          typeof( defaultValue ) === "object" ? this.push( Object.create( defaultValue ) ) : this.push( defaultValue );
        }
        this.length = newSize;
      }
      

      我检查了defaultValue 的类型,因为如果它是一个对象并且你只是将它推送到新元素,你最终会得到一个新元素指向同一个对象的数组。这意味着如果您在数组的一个元素中更改该对象的属性,所有其他元素也将更改。但是,如果您的 defaultValue 是原始元素,您可以安全地将其推送到新元素。

      【讨论】:

        【解决方案6】:

        由于Array.prototype.fill(请参阅链接页面底部的浏览器兼容性)或使用了 polyfill,此解决方案仅适用于新浏览器(IE 除外)。

        function resize(arr, newSize, defaultValue) {
            var originLength = arr.length; // cache original length
        
            arr.length = newSize; // resize array to newSize
        
            (newSize > originLength) && arr.fill(defaultValue, originLength); // Use Array.prototype.fill to insert defaultValue from originLength to the new length
        }
        

        【讨论】:

        • 它是 ES6,所以多年来不会兼容所有浏览器。
        • 它适用于 firefox 和 Safari(comp 表 - 无法检查),并且适用于下一版本的 Chrome。也许边缘到。但是,由于它现在不适用于所有浏览器,因此我已经开始使用免责声明来回答。
        【解决方案7】:

        调整一次数组的大小比执行大量.pushes 效率。这可以通过Array.prototype.length上的setter feature来完成:

        function resize(arr, newLength, defaultValue) {
          const oldLength = arr.length;
          arr.length = newLength;
          if (newLength > oldLength && typeof(defaultValue) !== 'undefined') {
            for (let i = oldLength; i < newLength; i++) {
              arr[i] = defaultValue;
              // Note: this will create many references to the same object, which
              // may not be what you want. See other answers to this question for 
              // ways to prevent this.
            }
          }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-11-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-08-05
          • 2020-08-23
          相关资源
          最近更新 更多