将值添加到数组的最有效方法

sam*_*one 249 javascript arrays prepend

假设我有一个大小为N(where N > 0)的数组,是否有一种更有效的方法可以预先添加到不需要O(N + 1)步的数组?

在代码中,基本上,我目前正在做的是

function prependArray(value, oldArray) {
  var newArray = new Array(value);

  for(var i = 0; i < oldArray.length; ++i) {
    newArray.push(oldArray[i]);
  }

  return newArray;
}
Run Code Online (Sandbox Code Playgroud)

mae*_*ics 462

我不确定在big-O方面效率更高,但肯定使用该unshift方法更简洁:

var a = [1, 2, 3, 4];
a.unshift(0);
a; // => [0, 1, 2, 3, 4]
Run Code Online (Sandbox Code Playgroud)

[编辑]

这个jsPerf基准测试显示,unshift至少在几个浏览器中速度要快得多,不管可能有不同的大O性能,如果你可以就地修改阵列.如果你真的无法改变原始数组,那么你会做类似下面的代码片段,它似乎没有比你的解决方案快得多:

a.slice().unshift(0); // Use "slice" to avoid mutating "a".
Run Code Online (Sandbox Code Playgroud)

[编辑2]

为了完整起见,可以使用以下函数代替OP的示例prependArray(...)来利用Array unshift(...)方法:

function prepend(value, array) {
  var newArray = array.slice();
  newArray.unshift(value);
  return newArray;
}

var x = [1, 2, 3];
var y = prepend(0, x);
y; // => [0, 1, 2, 3];
x; // => [1, 2, 3];
Run Code Online (Sandbox Code Playgroud)

  • 谁决定打电话给'prepend`"`unshift`"? (165认同)
  • @ScottStafford`unshift`听起来更适合这样的数组操作(它移动元素......或多或少地物理上).`prepend`更适合链接列表,你可以在其中添加元素. (12认同)
  • 不移位是转移的补充功能.称之为"前置"将是一个奇怪的选择.随着推动流行,Unshift将转移. (10认同)
  • 这个解决方案只有一个问题.unhift()不会返回它的**长度**,而不是**这个答案中的**数组**http://www.w3schools.com/jsref/jsref_unshift.asp (9认同)
  • `push`和`unshift`都包含`u`,而`pop`和`shift`都没有. (4认同)
  • 就像@iDVB所说 - 你需要分配切片数组,以便能够在`unshift`之后访问它.所以`a.slice(0).unshift(0)`没有意义.修复了答案. (2认同)

Fra*_*Tan 56

使用ES6,您现在可以使用spread运算符创建一个新数组,并在原始元素之前插入新元素.

// Prepend a single item.
const a = [1, 2, 3];
console.log([0, ...a]);
Run Code Online (Sandbox Code Playgroud)

// Prepend an array.
const a = [2, 3];
const b = [0, 1];
console.log([...b, ...a]);
Run Code Online (Sandbox Code Playgroud)

更新2018-08-17:性能

我打算在这个答案中提出一种我认为更难忘和简洁的替代语法.应该注意的是,根据一些基准测试(参见其他答案),这种语法明显变慢.除非您在循环中执行许多这些操作,否则这可能不重要.

  • 这应该在2017年投票.它简洁明了.通过使用spread,它会返回新的流,这可用于进一步修改链.它也是纯粹的(意味着a和b不受影响) (4认同)

mgi*_*uca 45

如果您将数组添加到另一个数组的前面,则使用它会更有效concat.所以:

var newArray = values.concat(oldArray);
Run Code Online (Sandbox Code Playgroud)

但这仍然是oldArray大小的O(N).不过,它比手动迭代oldArray更有效.此外,根据细节,它可能会对您有所帮助,因为如果您要预先添加许多值,最好先将它们放入数组中,然后在最后将oldArray连接起来,而不是单独添加每个值.

在oldArray的大小中,没有办法比O(N)做得更好,因为数组存储在连续的内存中,第一个元素处于固定位置.如果要在第一个元素之前插入,则需要移动所有其他元素.如果您需要解决此问题,请执行@GWW所说的内容并使用链接列表或不同的数据结构.

  • 哦,是的,我忘了"不合时宜".但请注意a)变异oldArray而`concat`不变(所以哪一个更适合你取决于具体情况),b)它只插入一个元素. (2认同)
  • 哇,这要慢得多。好吧,正如我所说,它正在制作数组的副本(并创建一个新数组 `[0]`),而 unshift 是在原地改变它。但两者都应该是 O(N)。也为该网站的链接欢呼——看起来非常方便。 (2认同)
  • 它对 oneliner 有好处,因为 concat 返回数组,而 unshift 返回新长度 (2认同)

小智 32

如果您想要添加数组(a1与数组a2),您可以使用以下内容:

var a1 = [1, 2];
var a2 = [3, 4];
Array.prototype.unshift.apply(a1, a2);
console.log(a1);
// => [3, 4, 1, 2]
Run Code Online (Sandbox Code Playgroud)


car*_*sba 8

以不可变的方式,这可能是最好的方式:

const x = 1
const list = [2, 3, 4]
const newList = [x].concat(list) // [1, 2, 3, 4]
Run Code Online (Sandbox Code Playgroud)


Evg*_*mov 7

我对不同的前置方法进行了一些新的测试。对于小型数组(<1000 个元素),领导者是 for 循环与 Push 方法相结合。对于大型数组,Unshift 方法成为领导者。

但这种情况仅适用于 Chrome 浏览器。在 Firefox 中,unshift 具有出色的优化,并且在所有情况下都更快。

ES6 在所有浏览器中的传播速度慢了 100 倍以上。

https://jsbench.me/cgjfc79bgx/1

  • 很好的答案!但为了确保您不会丢失其中的一部分,您可以在此处重现您的测试用例(可能带有一些示例运行结果),以防万一,例如 jsbench 消失。 (2认同)

ken*_*bec 6

如果需要保留旧数组,请将旧数组切片并将新值取消移动到切片的开头.

var oldA=[4,5,6];
newA=oldA.slice(0);
newA.unshift(1,2,3)

oldA+'\n'+newA

/*  returned value:
4,5,6
1,2,3,4,5,6
*/
Run Code Online (Sandbox Code Playgroud)


reh*_*001 6

调用unshift只返回新数组的长度。因此,要在开头添加一个元素并返回一个新数组,我这样做了:

let newVal = 'someValue';
let array = ['hello', 'world'];
[ newVal ].concat(array);
Run Code Online (Sandbox Code Playgroud)

或者简单地使用扩展运算符:

[ newVal, ...array ]
Run Code Online (Sandbox Code Playgroud)

这样,原始数组保持不变。