ChatGPT解决这个技术问题 Extra ChatGPT

创建零填充 JavaScript 数组的最有效方法是什么?

在 JavaScript 中创建任意长度的零填充数组的最有效方法是什么?

这方面的一些实际数据:jsperf.com/zeroarrayjs
ES6 fill 允许本机执行此操作。
arr = new Array(length+1).joint(character).split('');
2016 年更新:此处的另一个自定义基准:jsfiddle.net/basickarl/md5z0Lqq
let i = 0; Array.from(Array(10), ()=>i++);

B
Brian Burns

ES6 引入了 Array.prototype.fill。它可以这样使用:

new Array(len).fill(0);

不确定它是否很快,但我喜欢它,因为它很短且可以自我描述。

它仍然不在 IE (check compatibility) 中,但有一个 polyfill available


填充速度很快。 new Array(len) 非常缓慢。 (arr = []).length = len; arr.fill(0); 是我在任何地方看到的最快的解决方案......或者至少并列
@PimpTrizkit arr = Array(n)(arr = []).length = n 根据规范表现相同。在某些实现中,可能会更快,但我不认为有很大的不同。
...我承认我错过了这部分...当我将第二行添加到测试中时... arr.fill(0) ...一切都发生了变化。现在,在大多数情况下使用 new Array() 会更快,除非您获得数组大小 > 100000...然后你可以再次开始看到速度增加。但是,如果您实际上不必用零预填充它并且可以使用空数组的标准错误。然后 (arr = []).length = x 在我的测试用例中大部分时间都快疯了。
请注意,要遍历数组(例如 map 或 forEach),值必须设置,否则它将跳过这些索引。你设置的值可以是任何你想要的——甚至是未定义的。示例:尝试 new Array(5).forEach(val => console.log('hi'));new Array(5).fill(undefined).forEach(val => console.log('hi'));
当数组变得非常大时,我看到 fill() 比 for 循环慢很多:jsperf.com/zero-filling-large-arrays 而且 new Array(n)a = []; a.length = n 之间没有显着差异
z
zertosh

尽管这是一个旧线程,但我想在其中添加 2 美分。不知道这有多慢/快,但它是一个快速的单线。这是我所做的:

如果我想预先填写一个数字:

Array.apply(null, Array(5)).map(Number.prototype.valueOf,0);
// [0, 0, 0, 0, 0]

如果我想预填充一个字符串:

Array.apply(null, Array(3)).map(String.prototype.valueOf,"hi")
// ["hi", "hi", "hi"]

其他答案建议:

new Array(5+1).join('0').split('')
// ["0", "0", "0", "0", "0"]

但如果你想要 0(数字)而不是“0”(字符串中的零),你可以这样做:

new Array(5+1).join('0').split('').map(parseFloat)
// [0, 0, 0, 0, 0]

很好的答案!你能解释一下Array.apply(null, new Array(5)).map(...)的诀窍吗?因为简单地做 (new Array(5)).map(...) 不会像规范所说的那样工作
(顺便说一句,我们真的不需要 new)当您执行 Array(5) 时,您正在创建一个看起来像这样的对象:{ length: 5, __proto__: Array.prototype } - 尝试 console.dir( Array(5) )。请注意,它没有任何属性 012 等。但是当您将 apply 设置为 Array 构造函数时,就像是在说 Array(undefined, undefined, undefined, undefined, undefined)。你会得到一个看起来像 { length: 5, 0: undefined, 1: undefined...} 的对象。 map 适用于属性 01 等,这就是您的示例不起作用的原因,但是当您使用 apply 时它会起作用。
.apply 的第一个参数实际上是您想要的 this。对于这些目的,this 无关紧要 - 我们只真正关心 .apply 的参数传播“特征” - 所以它可以是任何值。我喜欢 null 因为它便宜,你可能不想使用 {}[] 因为你会无缘无故地实例化一个对象。
使用 size + assign 进行初始化也比 push 快得多。请参阅测试用例 jsperf.com/zero-fill-2d-array
Array.apply(null, Array(5)).map(x=>0) 呢?它有点短!
K
Kamil Kiełczewski

简而言之

最快的解决方案:

let a = new Array(n); for (let i=0; i<n; ++i) a[i] = 0;

最短(方便)解决方案(小型阵列慢 3 倍,大型阵列稍慢(Firefox 上最慢))

Array(n).fill(0)

细节

今天 2020.06.09 我在 Chrome 83.0、Firefox 77.0 和 Safari 13.1 浏览器上对 macOS High Sierra 10.13.6 进行了测试。我为两个测试用例测试选择的解决方案

小数组 - 有 10 个元素 - 你可以在这里执行测试

大数组 - 有 1M 元素 - 你可以在这里执行测试

结论

基于 new Array(n)+for (N) 的解决方案是小阵列和大阵列的最快解决方案(Chrome 除外,但仍然非常快),建议作为快速跨浏览器解决方案

基于 new Float32Array(n) (I) 的解决方案返回非典型数组(例如,您不能对其调用 push(..)),因此我不会将其结果与其他解决方案进行比较 - 但是此解决方案比其他解决方案快 10-20 倍适用于所有浏览器上的大数组

基于 for (L,M,N,O) 的解决方案对于小型阵列来说很快

基于填充 (B,C) 的解决方案在 Chrome 和 Safari 上速度很快,但在 Firefox 上对于大型阵列的速度却出奇地慢。它们对于小型阵列来说是中等速度

基于 Array.apply (P) 的解决方案为大数组函数 P(n) { return Array.apply(null, Array(n)).map(Number.prototype.valueOf,0) 抛出错误; } 尝试 { P(1000000); } 捕捉(e){ 控制台.错误(e.message); }

https://i.stack.imgur.com/ISmIZ.png

代码和示例

下面的代码介绍了测量中使用的解决方案

函数 A(n) { return [...new Array(n)].fill(0); } function B(n) { return new Array(n).fill(0); } 函数 C(n) { 返回 Array(n).fill(0); } function D(n) { return Array.from({length: n}, () => 0); } function E(n) { return [...new Array(n)].map(x => 0); } // 类型为 function F(n) { return Array.from(new Int32Array(n)); 的数组} 函数 G(n) { return Array.from(new Float32Array(n)); } 函数 H(n) { return Array.from(new Float64Array(n)); // 需要比 float32 多 2 倍的内存 } function I(n) { return new Float32Array(n); // 这不是典型的数组 } function J(n) { return [].slice.apply(new Float32Array(n)); } // 基于 for function K(n) { let a = []; a.长度 = n;让我 = 0;而 (i < n) { a[i] = 0;我++; } 返回一个; } 函数 L(n) { 让 a=[]; for(让 i=0; i { let a = f(10); console.log(`${f.name} length=${a.length}, arr[0]=${a[0]}, arr[9]=$ {a[9]}`) });此片段仅显示使用过的代码

Chrome 的示例结果:

https://i.stack.imgur.com/VRfay.png


刚刚在 Chrome 77 上运行了一些测试,使用 push() 的简单循环比 fill() 快两倍...我想知道 fill() 的哪些微妙副作用会阻止更有效的实现?
@EricGrange 我更新了答案 - 在底部我用你的提议更新了 benchamrk 的链接:案例 P let a=[]; for(i=n;i--;) a.push(0); - 但它比 fill(0) 慢 4 倍 - 所以我什至不会更新那个案例的图片。
漂亮的测量。分析:G之所以慢,是因为每次迭代都会调整数组的大小,而调整大小意味着进行新的内存分配。 A,B,M 快,因为尺寸调整只进行一次。 +1
@Roland我认为您的意思是N而不是M?
for-loop (N) 在 Safari 中仅比 .fill (C) 快 1.835,有趣的是,当我现在运行它时,6 个月后,差异已降至仅 1.456 倍。因此对于 Safari,最快的解决方案 (N) 仅比最短和最简单的版本快 45%。道德:坚持使用最短和最简单的版本(对于大多数情况,如果不是所有情况)。它节省了昂贵的开发人员时间,因为它的阅读速度更快,更易于维护,而且随着时间和 CPU 速度的增加,它的回报也越来越多,而无需额外的维护。
L
Lucio Paiva

用预先计算的值填充数组的优雅方法

这是另一种使用 ES6 的方法,到目前为止没有人提到过:

> Array.from(Array(3), () => 0)
< [0, 0, 0]

它通过将 map 函数作为 Array.from 的第二个参数传递来工作。

在上面的示例中,第一个参数分配了一个包含 3 个位置的数组,其中填充了值 undefined,然后 lambda 函数将每个位置映射到值 0

虽然 Array(len).fill(0) 更短,但如果您需要先进行一些计算来填充数组,它就不起作用了)。

例如,如果您需要一个包含 10 个随机数的数组:

> Array.from(Array(10), () => Math.floor(10 * Math.random()))
< [3, 6, 8, 1, 9, 3, 0, 6, 7, 1]

它比同等的更简洁(和优雅):

const numbers = Array(10);
for (let i = 0; i < numbers.length; i++) {
    numbers[i] = Math.round(10 * Math.random());
}

此方法还可用于通过利用回调中提供的索引参数来生成数字序列:

> Array.from(Array(10), (d, i) => i)
< [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

奖励答案:使用 String repeat() 填充数组

由于这个答案引起了很多关注,我也想展示这个很酷的技巧。虽然不如我的主要答案有用,但将介绍仍然不太为人所知但非常有用的 String repeat() 方法。这是诀窍:

> "?".repeat(10).split("").map(() => Math.floor(10 * Math.random()))
< [5, 6, 3, 5, 0, 8, 2, 7, 4, 1]

酷吧? repeat() 是一种非常有用的创建字符串的方法,该字符串将原始字符串重复一定次数。之后,split() 为我们创建一个数组,然后将其map()设置为我们想要的值。分步骤分解:

> "?".repeat(10)
< "??????????"

> "?".repeat(10).split("")
< ["?", "?", "?", "?", "?", "?", "?", "?", "?", "?"]

> "?".repeat(10).split("").map(() => Math.floor(10 * Math.random()))
< [5, 6, 3, 5, 0, 8, 2, 7, 4, 1]

那篇文章中有很多客厅技巧,但希望没有一个会达到生产代码:)
虽然在生产中绝对不需要 repeat 技巧,但 Array.from() 非常好:-)
不是真的,这里的 Array.from() 基本上是创建一个数组,用 map() 遍历它,在每个项目上调用一个函数来创建一个新数组,然后丢弃第一个数组......对于一个小数组,这可能是无害的,对于较大的数组,这种模式会导致人们称浏览器为“内存猪”:)
处理大型数组的人肯定比这更清楚。但是,对于常见的应用程序,创建将立即处理的常规大小的辅助数组(最多 10k 个元素)是非常好的(与避免创建额外数组一样花费相同的时间 - 使用最新的 Chrome 测试)。对于这样的情况,可读性变得比微小的性能优化更重要。关于 O(n) 时间,如果您需要为每个元素计算不同的东西(我的回答的主要主题),则有必要。这个讨论很有趣,很高兴你提出来!
G
Gerald Senarclens de Grancy

已经提到的 ES 6 填充方法很好地解决了这个问题。迄今为止,大多数现代桌面浏览器已经支持所需的 Array 原型方法(Chromium、FF、Edge 和 Safari)[1]。您可以在 MDN 上查找详细信息。一个简单的使用示例是

a = new Array(10).fill(0);

鉴于当前的浏览器支持,除非您确定您的受众使用现代桌面浏览器,否则您应该谨慎使用它。


如果您填写参考类型,它将是所有它们的相同参考。 new Array(10).fill(null).map(() => []) 将是解决此问题的一种简洁方法(最初烧死我哈哈)
2016 年更新:此方法将其他所有内容都从水中吹走,单击此处查看基准:jsfiddle.net/basickarl/md5z0Lqq
这将适用于数组。 a = Array(10).fill(null).map(() => { return []; });
@AndrewAnthonyGerst Terser:a = Array(10).fill(0).map( _ => [] );
T
T.J. Crowder

2013 年 8 月添加的注释,2015 年 2 月更新:以下 2009 年的答案与 JavaScript 的通用 Array 类型有关。它与 ES2015 中定义的较新的 typed 数组无关 [现在在许多浏览器中都可用],例如 Int32Array 等。另请注意,ES2015 为 Arraystyped arrays 添加了一个 fill 方法,这可能是填充它们的最有效方法...

此外,它可以对您如何创建数组的某些实现产生很大的影响。尤其是 Chrome 的 V8 引擎,如果它认为可以,它会尝试使用高效的连续内存数组,仅在必要时才转向基于对象的数组。

对于大多数语言,它将是预分配,然后是零填充,如下所示:

function newFilledArray(len, val) {
    var rv = new Array(len);
    while (--len >= 0) {
        rv[len] = val;
    }
    return rv;
}

但是,JavaScript 数组 aren't really arrays,它们是键/值映射,就像所有其他 JavaScript 对象一样,所以没有“预分配”要做(设置长度不会分配那么多槽填充),也没有任何理由相信倒数到零的好处(这只是为了使循环中的比较快速)在实现可能已经优化时以相反的顺序添加键不会超过他们处理与数组相关的键的理论通常会按顺序进行。

事实上,Matthew Crumley 指出,在 Firefox 上倒数比倒数要慢得多,我可以确认这一结果——它是数组的一部分(向下循环到零仍然比在 var 中循环到极限要快)。显然,以相反的顺序将元素添加到数组中是 Firefox 上的一个缓慢操作。事实上,结果因 JavaScript 实现而有很大差异(这并不令人惊讶)。这是一个用于浏览器实现的快速而肮脏的测试页面(如下)(非常肮脏,在测试期间不会产生,因此提供的反馈很少,并且会违反脚本时间限制)。我建议在测试之间刷新;如果您不这样做,FF(至少)会减慢重复测试的速度。

在 1,000 到 2,000 个元素数组之间的某个地方,使用 Array#concat 的相当复杂的版本比 FF 上的直接初始化更快。然而,在 Chrome 的 V8 引擎上,每次都是直接 init 胜出……

这是一个测试:

const tests = [ { name: "downpre", total: 0, desc: "倒计时,前递减", func: makeWithCountDownPre }, { name: "downpost", total: 0, desc: "倒计时,后递减decrement", func: makeWithCountDownPost }, { name: "up", total: 0, desc: "Count up (normal)", func: makeWithCountUp }, { name: "downandup", total: 0, desc: "倒计时(for循环)和up(填充)", func: makeWithCountDownArrayUp }, { name: "concat", total: 0, desc: "Concat", func: makeWithConcat } ]; const q = sel => document.querySelector(sel);让标记=“”; for (const {name, desc} of tests) { markup += `

`; } q("#checkboxes").innerHTML = 标记; q("#btnTest").addEventListener("click", btnTestClick); function btnTestClick() { // 清除日志 q("#log").innerHTML = "Testing..."; // 显示正在运行的 q("#btnTest").disabled = true; // 在浏览器更新显示时暂停后运行 setTimeout(btnTestClickPart2, 0); } 功能 btnTestClickPart2() { 尝试 { runTests(); } catch (e) { log(`Exception: ${e.message}`); } // 重新启用按钮 q("#btnTest").disabled = false; } function getNumField(name) { const val = q("#" + name).value.trim();常量数 = /^\d+$/.test(val) ? parseInt(val) : NaN; if (isNaN(num) || num <= 0) { throw new Error(`Invalid ${name} value ${JSON.stringify(val)}`); } 返回数字; } function runTests() { try { // 清除日志 q("#log").innerHTML = ""; const runCount = getNumField("loops");常量长度 = getNumField("长度"); // 这样做(我们运行 runCount + 1 次,第一次是热身) for (let counter = 0; counter <= runCount; ++counter) { for (const test of tests) { if (q("# chk_" + test.name).checked) { const start = Date.now(); const a = test.func(length); const time = Date.now() - 开始; if (counter == 0) { // 不计算(预热),但要检查算法是否有效 const invalid = validateResult(a, length); if (invalid) { log(`FAILURE with test ${test.name}: ${invalid}`);返回; } } else { // 统计这条日志(`#${counter}: ${test.desc}: ${time}ms`); test.total += 时间; } } } } for (const test of tests) { if (q("#chk_" + test.name).checked) { test.avg = test.total / runCount;如果(类型最低!=“数字”||最低> test.avg){最低= test.avg; } } } let results = "

Results:" + "
Length: " + length + "
Loops: " + runCount + "

"; for (const test of tests) { if (q("#chk_" + test.name).checked) { results += `

${test.desc},平均时间:${test.avg}ms

`; } } 结果 += "
"; q("#log").insertAdjacentHTML("afterbegin", results); } 捕捉 (e) { 日志(e.message);返回; } } function validateResult(a, length) { if (a.length != length) { return "Length is wrong"; } for (let n = length - 1; n >= 0; --n) { if (a[n] != 0) { return "Index " + n + " is not zero"; } } 返回未定义; } function makeWithCountDownPre(len) { const a = new Array(len);而 (--len >= 0) { a[len] = 0; } 返回一个; } 函数 makeWithCountDownPost(len) { const a = new Array(len);而 (len-- > 0) { a[len] = 0; } 返回一个; } function makeWithCountUp(len) { const a = new Array(len); for (让 i = 0; i < len; ++i) { a[i] = 0; } 返回一个; } 函数 makeWithCountDownArrayUp(len) { const a = new Array(len);让我 = 0;而 (--len >= 0) { a[i++] = 0; } 返回一个; } 函数 makeWithConcat(len) { if (len == 0) { return []; } 让 a = [0];让currlen = 1; while (currlen < len) { const rem = len - currlen; if (rem < currlen) { a = a.concat(a.slice(0, rem)); } else { a = a.concat(a); } currlen = a.length; } 返回一个; } 功能日志(味精) { 常量 p = document.createElement("p"); p.textContent = 味精; q("#log").appendChild(p); } 正文 { 字体系列:无衬线; } #log p { 边距:0;填充:0; } .error { 颜色:红色; } .winner { 颜色:绿色; }


不确定向后填充在这里是否重要,因为您只是访问元素(而不是删除它们)并且您已经预先分配了。我错了吗?
向后填充的点与数组无关,它与 while 的转义条件有关 - falsey 0 非常有效地终止循环
(虽然我刚刚注意到这段代码实际上并没有使用它)
@annakata,你不能在这里使用它,因为 0 是一个有效的索引。
@triptych:不正确,只需要正确的顺序 - 见我的帖子
f
foxiris

如果你使用 ES6,你可以像这样使用 Array.from()

Array.from({ length: 3 }, () => 0);
//[0, 0, 0]

具有相同的结果

Array.from({ length: 3 }).map(() => 0)
//[0, 0, 0]

因为

Array.from({ length: 3 })
//[undefined, undefined, undefined]

C
Community

默认情况下,Uint8ArrayUint16ArrayUint32Array 类将零作为其值,因此您不需要任何复杂的填充技术,只需执行以下操作:

var ary = new Uint8Array(10);

默认情况下,数组 ary 的所有元素都为零。


这很好,但请注意,这不能与普通数组一样对待,例如 Array.isArray(ary)false。该长度也是只读的,因此您不能像 ary.push 那样将新项目推送到它
Fwiw 所有类型化数组都将 0 保留为其默认值。
@MusikAnimal,Array.from(new Uint8Array(10)) 将提供一个普通数组。
@TomasLangkaas:是的,但是如果您真正需要的是 JS 数组,another answer 表明它比 Chrome 中的 Array(n).fill(0) 慢 5 倍。如果您可以使用 TypedArray,这甚至比 .fill(0) 快得多,特别是如果您可以使用默认的初始值设定项 0。似乎没有像 C++ std::vector 那样采用填充值和长度的构造函数。似乎对于任何非零值,您都必须构造一个归零的 TypedArray 并 then 填充它。 :/
k
kangax
function makeArrayOf(value, length) {
  var arr = [], i = length;
  while (i--) {
    arr[i] = value;
  }
  return arr;
}

makeArrayOf(0, 5); // [0, 0, 0, 0, 0]

makeArrayOf('x', 3); // ['x', 'x', 'x']

请注意,while 通常比 for-inforEach 等更有效。


i 局部变量不是无关的吗? length 是按值传递的,因此您应该能够直接递减它。
虽然起初看起来不错,但不幸的是,在数组中的任意点(例如 arr[i] = value)分配值非常慢。从头到尾循环并使用 arr.push(value) 会快得多。这很烦人,因为我更喜欢你的方法。
A
Allen Rice

使用对象符号

var x = [];

零填充?喜欢...

var x = [0,0,0,0,0,0];

充满'未定义'......

var x = new Array(7);

带零的 obj 表示法

var x = [];
for (var i = 0; i < 10; i++) x[i] = 0;

附带说明一下,如果您修改 Array 的原型,

var x = new Array();

var y = [];

将有那些原型修改

无论如何,我不会过分关心这个操作的效率或速度,你可能会做很多其他的事情,这些事情比实例化包含零的任意长度的数组更加浪费和昂贵。


错误...此数组中没有 null - var x = new Array(7);
实际上,数组并没有用 new Array(n) 填充任何东西,甚至没有“未定义”,它只是将数组长度值设置为 n。您可以通过调用 (new Array(1)).forEach(...) 来检查这一点。 forEach 从不执行,不像你在 [undefined] 上调用它。
new Array(7)创建一个“充满未定义”的数组。它创建一个长度为 7 的 empty 数组。
您可能想重新考虑部分答案,因为@RobG 所说的很重要(如果您所说的是真的,映射会容易得多)
这些天你可以做(new Array(10)).fill(0)
M
Matthew Crumley

我已经在 IE 6/7/8、Firefox 3.5、Chrome 和 Opera 中测试了预分配/非预分配、向上/向下计数和 for/while 循环的所有组合。

下面的函数在 Firefox、Chrome 和 IE8 中一直是最快或非常接近的,并且不比 Opera 和 IE 6 中的最快慢多少。这也是我认为最简单和最清晰的。我发现几个浏览器的 while 循环版本稍快一些,所以我也将其包括在内以供参考。

function newFilledArray(length, val) {
    var array = [];
    for (var i = 0; i < length; i++) {
        array[i] = val;
    }
    return array;
}

或者

function newFilledArray(length, val) {
    var array = [];
    var i = 0;
    while (i < length) {
        array[i++] = val;
    }
    return array;
}

实际上,您也可以将 var array = [] 声明放入 for 循环的第一部分,仅用逗号分隔。
我喜欢 damianb 的建议,但请记住将赋值和逗号放在递增之前! `for (var i = 0; i < 长度; array[i] = val, i++);
做其他人都缺少的第二个,并将数组的长度设置为已经给出的 length 值,这样它就不会不断变化。在我的机器上带来了一个 100 万长度的零数组,从 40ms 到 8。
当我将此解决方案重构为单列时,我的速度似乎提高了 10-15%。 for (i = 0, array = []; i < length; ++i) array[i] = val;.. 更少的块? ......无论如何,还有......如果我将新数组的array.length设置为长度......我似乎在FF中获得了另外10%-15%的速度提升......在Chrome中,它似乎加倍速度-> var i, array = []; array.length = length; while(i < length) array[i++] = val;(如果我将其保留为 for 循环会更快......但不再需要 init,因此 while 在此版本上似乎更快)
我也会在我的测试中注意到这一点。在我的大量测试用例中,上面的最终版本的执行速度似乎快了 3 倍到 10 倍以上……我不太清楚为什么……(在 chrome 和 FF 之间测试了不同的数组大小)
V
Vic

ES6 解决方案:

[...new Array(5)].map(x => 0); // [0, 0, 0, 0, 0]

а
аlex dykyі

const arr = Array.from({ length: 10 }).fill(0);控制台日志(arr)


N
Nenad Vukicevic

如果您需要在代码执行期间创建许多不同长度的零填充数组,我发现实现此目的的最快方法是使用本主题中提到的方法之一创建一次零数组,长度为您知道永远不会超过,然后根据需要对该数组进行切片。

例如(使用上面选择的答案中的函数来初始化数组),创建一个长度为 maxLength 的零填充数组,作为需要零数组的代码可见的变量:

var zero = newFilledArray(maxLength, 0);

现在,每当您需要一个长度为 requiredLength < maxLength 的零填充数组时,都对该数组进行切片:

zero.slice(0, requiredLength);

在执行代码期间,我创建了数千次零填充数组,这极大地加快了进程。


E
Eli
function zeroFilledArray(size) {
    return new Array(size + 1).join('0').split('');
}

您也可以使用 new Array(size+1).join("x").split("x").map(function() { return 0; }) 来获取实际数字
@Yuval 或者只是 new Array(size+1).join('0').split('').map(Number)
d
djechlin

使用 lodashunderscore

_.range(0, length - 1, 0);

或者如果你有一个数组并且你想要一个相同长度的数组

array.map(_.constant(0));

很高兴你添加了这个答案,因为我使用下划线,而且我知道这有一些东西......但还没有找到它。我只是希望我可以使用它创建对象数组
@PandaWood _.range(0, length -1, 0).map(Object.new),我想。
我相信应该是_.range(0, length, 0)。 Lodash 不包含最终价值
S
Salvador Dali

我不反对:

Array.apply(null, Array(5)).map(Number.prototype.valueOf,0);
new Array(5+1).join('0').split('').map(parseFloat);

由 Zertosh 建议,但在 new ES6 数组扩展中允许您使用 fill 方法本机执行此操作。现在 IE edge、Chrome 和 FF 都支持它,但请检查 compatibility table

new Array(3).fill(0)会给你[0, 0, 0]。您可以使用 new Array(5).fill('abc') 之类的任何值(甚至是对象和其他数组)填充数组。

最重要的是,您可以使用填充修改以前的数组:

arr = [1, 2, 3, 4, 5, 6]
arr.fill(9, 3, 5)  # what to fill, start, end

这给了你:[1, 2, 3, 9, 9, 6]


J
Juanma Menendez

创建一个全新的数组

new Array(arrayLength).fill(0);

在现有数组的末尾添加一些值

[...existingArray, ...new Array(numberOfElementsToAdd).fill(0)]

例子

//**创建一个全新的数组** console.log(new Array(5).fill(0)); //**要在现有数组的末尾添加一些值** let existingArray = [1,2,3] console.log([...existingArray, ...new Array(5).fill(0) ]);


d
durum

我通常这样做(而且速度惊人)的方式是使用 Uint8Array。例如,创建一个由 1M 个元素组成的零填充向量:

  var zeroFilled = [].slice.apply(new Uint8Array(1000000))

我是 Linux 用户,一直为我工作,但曾经有一个朋友使用 Mac 有一些非零元素。我以为他的机器出了故障,但仍然是我们找到的最安全的修复方法:

  var zeroFilled = [].slice.apply(new Uint8Array(new Array(1000000)) 

已编辑

铬 25.0.1364.160

Frederik Gottlieb - 6.43 Sam Barnum - 4.83 Eli - 3.68 Joshua 2.91 Mathew Crumley - 2.67 bduran - 2.55 Allen Rice - 2.11 kangax - 0.68 Tj。克劳德 - 0.67 zertosh - 错误

火狐 20.0

Allen Rice - 1.85 Joshua - 1.82 Mathew Crumley - 1.79 bduran - 1.37 Frederik Gottlieb - 0.67 Sam Barnum - 0.63 Eli - 0.59 kagax - 0.13 Tj。克劳德 - 0.13 zertosh - 错误

缺少最重要的测试(至少对我而言):Node.js 测试。我怀疑它接近 Chrome 基准。


这是对我的手指和眼睛最有效的方法。但是对于 Chrome 来说它非常非常慢(根据那个 jsperf。慢了 99%)。
我想知道您朋友 Mac 上的问题是否与:stackoverflow.com/questions/39129200/… 或 Array.slice 没有处理 UInt8Array 并泄漏未初始化的内存有关? (安全问题!)。
@robocat 好收获!如果我记得很清楚,我们使用的是 Node.js 0.6 或 0.8。我们考虑过某种泄漏,但我们无法用生产堆栈重现它,所以我们决定忽略它。
I
Isaac B

ECMAScript2016 开始,大型阵列只有一个明确的选择。

由于这个答案仍然显示在谷歌搜索的顶部附近,这是 2017 年的答案。

这是当前的jsbench,其中包含几十种流行的方法,包括迄今为止针对此问题提出的许多方法。如果您找到更好的方法,请添加、分叉和分享。

我想指出,没有真正最有效的方法来创建任意长度的零填充数组。您可以针对速度进行优化,或者针对清晰度和可维护性进行优化——根据项目的需要,两者都可以被认为是更有效的选择。

在优化速度时,您希望: 使用文字语法创建数组;设置长度,初始化迭代变量,并使用 while 循环遍历数组。这是一个例子。

常量 arr = []; arr.length = 120000;让我 = 0;而 (i < 120000) { arr[i] = 0;我++; }

另一种可能的实现是:

(arr = []).length = n;
let i = 0;
while (i < n) {
    arr[i] = 0;
    i++;
}

但我强烈反对在实践中使用第二次植入,因为它不太清楚,并且不允许您在数组变量上维护块范围。

这些比用 for 循环填充要快得多,比标准方法快约 90%

const arr = Array(n).fill(0);

但这种填充方法仍然是较小阵列最有效的选择,因为它清晰、简洁和可维护。除非您制作大量长度为数千或更多的数组,否则性能差异可能不会杀死您。

其他一些重要的注意事项。大多数样式指南建议您在使用 ES6 或更高版本时不要再使用var,除非有非常特殊的原因。将 const 用于不会重新定义的变量,将 let 用于将要重新定义的变量。 MDNAirbnb's Style Guide 是获取有关最佳做法的更多信息的好地方。这些问题与语法无关,但重要的是,JS 新手在搜索这些新旧答案时了解这些新标准。


E
Eugene Tiurin

没有在答案中看到这种方法,所以这里是:

"0".repeat( 200 ).split("").map( parseFloat )

结果,您将获得长度为 200 的零值数组:

[ 0, 0, 0, 0, ... 0 ]

我不确定这段代码的性能,但如果你将它用于相对较小的数组,这应该不是问题。


既不是最快的也不是最短的,但对解决方案的多样性做出了很好的贡献。
d
djechlin

new Array(51).join('0').split('') 呢?


然后 .map(function(a){return +a})
至于 2020 年,new Array(51).fill(0)呢?它提供完全相同的输出。
"0".repeat(100000000).split(''); 明显快于所有其他方法。
T
Tomiwa Adefokun

让填充 = [];填充长度 = 10;填充.填充(0);控制台.log(填充);


S
Sam Barnum

在我在 Chrome (2013-03-21) 上的测试中,这个 concat 版本要快得多。 10,000,000 个元素大约需要 200 毫秒,而直接初始化大约需要 675 毫秒。

function filledArray(len, value) {
    if (len <= 0) return [];
    var result = [value];
    while (result.length < len/2) {
        result = result.concat(result);
    }
    return result.concat(result.slice(0, len-result.length));
}

奖励:如果您想用字符串填充数组,这是一种简洁的方法(虽然不如 concat 快):

function filledArrayString(len, value) {
    return new Array(len+1).join(value).split('');
}

好吧,狂野。这比使用 new Array(len) 快得多。但!我在 Chrome 中看到后续读取该数据需要更长的时间。这里有一些时间戳来说明我的意思:(使用新数组(len))0.365:制作数组4.526:执行卷积10.75:卷积完成(使用concat)0.339:制作数组0.591:执行卷积//OMG,方式更快18.056:卷积完成
F
Frederik Gottlieb

我正在测试 TJ Crowder 的出色答案,并提出了基于 concat 解决方案的递归合并,该解决方案优于他在 Chrome 中的任何测试(我没有测试其他浏览器)。

function makeRec(len, acc) {
    if (acc == null) acc = [];
    if (len <= 1) return acc;
    var b = makeRec(len >> 1, [0]);
    b = b.concat(b);
    if (len & 1) b = b.concat([0]);
    return b;
},

使用 makeRec(29) 调用该方法。


I
Ivo

值得指出的是,Array.prototype.fill 已作为 ECMAScript 6 (Harmony) proposal 的一部分添加。在考虑线程中提到的其他选项之前,我宁愿使用下面写的 polyfill。

if (!Array.prototype.fill) {
  Array.prototype.fill = function(value) {

    // Steps 1-2.
    if (this == null) {
      throw new TypeError('this is null or not defined');
    }

    var O = Object(this);

    // Steps 3-5.
    var len = O.length >>> 0;

    // Steps 6-7.
    var start = arguments[1];
    var relativeStart = start >> 0;

    // Step 8.
    var k = relativeStart < 0 ?
      Math.max(len + relativeStart, 0) :
      Math.min(relativeStart, len);

    // Steps 9-10.
    var end = arguments[2];
    var relativeEnd = end === undefined ?
      len : end >> 0;

    // Step 11.
    var final = relativeEnd < 0 ?
      Math.max(len + relativeEnd, 0) :
      Math.min(relativeEnd, len);

    // Step 12.
    while (k < final) {
      O[k] = value;
      k++;
    }

    // Step 13.
    return O;
  };
}

n
nathnolt

最短 for 循环代码

a=i=[];for(;i<100;)a[i++]=0;

edit:
for(a=i=[];i<100;)a[i++]=0;
or
for(a=[],i=100;i--;)a[i]=0;

安全变量版本

var a=[],i=0;for(;i<100;)a[i++]=0;

edit:
for(var i=100,a=[];i--;)a[i]=0;

鉴于长度是一个定义的变量 n,这会更短:for(var a=[];n--;a[n]=0);
J
Joshua

我最快的功能是:

function newFilledArray(len, val) {
    var a = [];
    while(len--){
        a.push(val);
    }
    return a;
}

var st = (new Date()).getTime();
newFilledArray(1000000, 0)
console.log((new Date()).getTime() - st); // returned 63, 65, 62 milliseconds

使用本机 push 和 shift 将项目添加到数组中比声明数组范围并引用每个项目来设置它的值要快得多(大约 10 倍)。

仅供参考:当我在 firebug(firefox 扩展)中运行它时,我总是在第一个循环中获得更快的时间,这是倒计时。

var a = [];
var len = 1000000;
var st = (new Date()).getTime();
while(len){
    a.push(0);
    len -= 1;
}
console.log((new Date()).getTime() - st); // returned 863, 894, 875 milliseconds
st = (new Date()).getTime();
len = 1000000;
a = [];
for(var i = 0; i < len; i++){
    a.push(0);
}
console.log((new Date()).getTime() - st); // returned 1155, 1179, 1163 milliseconds

我很想知道 TJ Crowder 对此有何看法? :-)


您可以通过将其更改为 while (len--) 来加快速度。我的处理时间从大约 60 毫秒到大约 54 毫秒
Matthew Crumbly 的回答实际上仍然超过了这个(30 毫秒)!
a
annakata

我知道我在某个地方有这个原型:)

Array.prototype.init = function(x,n)
{
    if(typeof(n)=='undefined') { n = this.length; }
    while (n--) { this[n] = x; }
    return this;
}

var a = (new Array(5)).init(0);

var b = [].init(0,4);

编辑:测试

作为对 Joshua 和其他方法的回应,我运行了自己的基准测试,我看到的结果与报告的完全不同。

这是我测试的:

//my original method
Array.prototype.init = function(x,n)
{
    if(typeof(n)=='undefined') { n = this.length; }
    while (n--) { this[n] = x; }
    return this;
}

//now using push which I had previously thought to be slower than direct assignment
Array.prototype.init2 = function(x,n)
{
    if(typeof(n)=='undefined') { n = this.length; }
    while (n--) { this.push(x); }
    return this;
}

//joshua's method
function newFilledArray(len, val) {
    var a = [];
    while(len--){
        a.push(val);
    }
    return a;
}

//test m1 and m2 with short arrays many times 10K * 10

var a = new Date();
for(var i=0; i<10000; i++)
{
    var t1 = [].init(0,10);
}
var A = new Date();

var b = new Date();
for(var i=0; i<10000; i++)
{
    var t2 = [].init2(0,10);
}
var B = new Date();

//test m1 and m2 with long array created once 100K

var c = new Date();
var t3 = [].init(0,100000);
var C = new Date();

var d = new Date();
var t4 = [].init2(0,100000);
var D = new Date();

//test m3 with short array many times 10K * 10

var e = new Date();
for(var i=0; i<10000; i++)
{
    var t5 = newFilledArray(10,0);
}
var E = new Date();

//test m3 with long array created once 100K

var f = new Date();
var t6 = newFilledArray(100000, 0)
var F = new Date();

结果:

IE7 deltas:
dA=156
dB=359
dC=125
dD=375
dE=468
dF=412

FF3.5 deltas:
dA=6
dB=13
dC=63
dD=8
dE=12
dF=8

因此,据我估计,通常情况下推送确实较慢,但在 FF 中使用更长的数组时表现更好,但在 IE 中表现更差,这通常很糟糕(令人惊讶)。


我刚刚对此进行了测试:第二种方法 (b = []...) 比第一种方法快 10-15%,但比 Joshua 的答案慢 10 倍以上。
我知道这是一篇古帖。但也许其他人(比如我)仍然感兴趣。因此,我想建议对原型函数进行补充:在 this.length-check 之后包含一个 else {this.length=n;}。如果需要,这将在将现有数组重新init 化为不同长度 n 时缩短它。
M
Mateo Gianolio

匿名函数:

(function(n) { while(n-- && this.push(0)); return this; }).call([], 5);
// => [0, 0, 0, 0, 0]

用for循环稍微短一点:

(function(n) { for(;n--;this.push(0)); return this; }).call([], 5);
// => [0, 0, 0, 0, 0]

适用于任何 Object,只需更改 this.push() 中的内容。

您甚至可以保存函数:

function fill(size, content) {
  for(;size--;this.push(content));
  return this;
}

调用它使用:

var helloArray = fill.call([], 5, 'hello');
// => ['hello', 'hello', 'hello', 'hello', 'hello']

将元素添加到已经存在的数组中:

var helloWorldArray = fill.call(helloArray, 5, 'world');
// => ['hello', 'hello', 'hello', 'hello', 'hello', 'world', 'world', 'world', 'world', 'world']

性能:http://jsperf.com/zero-filled-array-creation/25


'0 '.repeat(200).split(' ')