A maneira mais eficiente de criar um array JavaScript preenchido com zero?

Qual é a maneira mais eficiente de criar um array com comprimento zero arbitrário em JavaScript?

Embora este seja um segmento antigo, eu queria adicionar meus 2 centavos para ele. Não tenho certeza o quão lento / rápido isso é, mas é um rápido um forro. Aqui está o que eu faço:

Se eu quiser preencher previamente com um número:

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

Se eu quiser preencher previamente com uma string:

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

Outras respostas sugeriram:

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

mas se você quiser 0 (o número) e não “0” (zero dentro de uma string), você pode fazer:

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

O ES6 apresenta o Array.prototype.fill . Pode ser usado assim:

 new Array(len).fill(0); 

Não tenho certeza se é rápido, mas eu gosto porque é curto e autodescritivo.

Ainda não está no IE ( verifique a compatibilidade ), mas há um polyfill disponível .

Maneira elegante de preencher uma matriz com valores pré-computados

Aqui está outra maneira de fazer isso usando ES6 que ninguém mencionou até agora:

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

Ele funciona passando uma function de mapa como o segundo parâmetro de Array.from .

No exemplo acima, o primeiro parâmetro aloca uma matriz de 3 posições preenchidas com o valor undefined e, em seguida, a function lambda mapeia cada uma delas para o valor 0 .

Embora Array(len).fill(0) seja mais curto, ele não funciona se você precisar preencher o array fazendo algum cálculo primeiro (eu sei que a pergunta não foi solicitada, mas muitas pessoas acabam aqui procurando por isso) .

Por exemplo, se você precisar de um array com 10 números randoms:

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

É mais conciso (e elegante) que o equivalente:

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

Esse método também pode ser usado para gerar seqüências de números, aproveitando o parâmetro de índice fornecido no retorno de chamada:

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

Resposta de bônus: preencha uma matriz usando String repeat()

Como essa resposta está chamando muita atenção, também queria mostrar esse truque legal. Embora não seja tão útil quanto a minha resposta principal, irá introduzir o método repeat() String ainda não muito conhecido, mas muito útil. Aqui está o truque:

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

Legal né? repeat() é um método muito útil para criar uma string que é a repetição da string original um certo número de vezes. Depois disso, split() cria um array para nós, que é então map() para os valores que queremos. Quebrando em etapas:

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

O já mencionado método de preenchimento ES 6 cuida disso bem. A maioria dos navegadores de desktop modernos já suportam os methods de protótipo Array necessários a partir de hoje (Chromium, FF, Edge e Safari) [ 1 ]. Você pode procurar detalhes sobre o MDN . Um exemplo de uso simples é

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

Dado o suporte atual do navegador, você deve ser cauteloso para usar isso, a menos que tenha certeza de que seu público-alvo usa navegadores modernos do Google Desktop.

Observação adicionada em agosto de 2013, atualizada em fevereiro de 2015: a resposta abaixo de 2009 está relacionada ao tipo de Array genérica do JavaScript. Ele não se relaciona com os arrays tipados mais novos definidos no ES2015 [e disponível agora em muitos navegadores], como Int32Array e outros. Observe também que o ES2015 adiciona um método de fill para matrizes e matrizes digitadas , o que provavelmente será a maneira mais eficiente de preenchê-las …

Além disso, pode fazer uma grande diferença para algumas implementações como você cria a matriz. O mecanismo V8 do Google Chrome, em particular, tenta usar um array de memory contígua altamente eficiente, se achar que pode, mudando para o array baseado em object apenas quando necessário.


Com a maioria das linguagens, seria pré-alocado, depois preenchido com zero, assim:

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

Mas , as matrizes JavaScript não são realmente matrizes , elas são mapas de chave / valor assim como todos os outros objects JavaScript, portanto não há “pré-alocação” para fazer (definir o tamanho não aloca muitos espaços para preencher), Existe alguma razão para acreditar que o benefício da contagem regressiva para zero (que é apenas para fazer a comparação no loop rápido) não é compensado pela adição das chaves na ordem inversa, quando a implementação pode ter otimizado sua manipulação das chaves relacionado a matrizes na teoria, você geralmente as faz em ordem.

Na verdade, Matthew Crumley apontou que a contagem regressiva é marcadamente mais lenta no Firefox do que a contagem, um resultado que posso confirmar – é a parte da matriz (looping até zero ainda é mais rápido do que um limite em um var). Aparentemente, adicionar os elementos ao array na ordem inversa é uma operação lenta no Firefox. De fato, os resultados variam um pouco pela implementação do JavaScript (o que não é surpreendente). Aqui está uma página de teste rápida e suja (abaixo) para implementações de navegador (muito suja, não produz durante os testes, portanto, fornece feedback mínimo e vai colidir com os limites de tempo de script). Eu recomendo atualizar entre os testes; FF (pelo menos) diminui em testes repetidos se você não fizer isso.

A versão bastante complicada que usa o Array # concat é mais rápida do que um init direto no FF, como em algum lugar entre 1.000 e 2.000 matrizes de elementos. No motor V8 do Chrome, porém, o init direto vence todas as vezes …

Aqui está a página de teste ( cópia ao vivo ):

     Zero Init Test Page     



Por padrão, as classs Uint8Array , Uint16Array e Uint32Array mantêm zeros como seus valores, portanto, você não precisa de nenhuma técnica complexa de preenchimento, apenas faça:

 var ary = new Uint8Array(10); 

Todos os elementos do array ary serão zeros por padrão.

 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'] 

Observe que, while geralmente seja mais eficiente do que for-in , forEach , etc.

Eu testei todas as combinações de pré-alocação / não pré-alocação, contando para cima / para baixo e para / while loops no IE 6/7/8, Firefox 3.5, Chrome e Opera.

As funções abaixo eram consistentemente as mais rápidas ou extremamente próximas no Firefox, Chrome e IE8, e não muito mais lentas que as mais rápidas no Opera e no IE 6. Também é a mais simples e clara na minha opinião. Eu encontrei vários navegadores em que a versão do loop while é um pouco mais rápida, então estou incluindo também para referência.

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

ou

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

Se você usa ES6, você pode usar Array.from () assim:

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

Tem o mesmo resultado que

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

Porque

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

usando notação de object

 var x = []; 

preenchido com zero? gostar…

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

preenchido com ‘indefinido’ …

 var x = new Array(7); 

notação obj com zeros

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

Como uma nota lateral, se você modificar o protótipo de Array, ambos

 var x = new Array(); 

e

 var y = []; 

terá essas modificações protótipo

De qualquer forma, eu não ficaria excessivamente preocupado com a eficiência ou a velocidade dessa operação, há muitas outras coisas que você provavelmente fará que são muito mais dispendiosas e dispendiosas do que instanciar uma série de comprimentos arbitrários contendo zeros.

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

Se você precisar criar muitos arrays preenchidos com zero de comprimentos diferentes durante a execução do seu código, a maneira mais rápida que encontrei para conseguir isso é criar um array zero uma vez , usando um dos methods mencionados neste tópico, de uma extensão que você sabe que nunca será excedido e, em seguida, divida essa matriz conforme necessário.

Por exemplo (usando a function da resposta escolhida acima para inicializar a matriz), crie uma matriz preenchida com zero de comprimento maxLength , como uma variável visível para o código que precisa de matrizes iguais a zero:

 var zero = newFilledArray(maxLength, 0); 

Agora, corte essa matriz sempre que precisar de uma matriz preenchida com zero de comprimento requiredLength < maxLength :

 zero.slice(0, requiredLength); 

Eu estava criando matrizes preenchidas zero milhares de vezes durante a execução do meu código, isso acelerou o processo tremendamente.

A maneira que eu costumo fazer isso (e é incrível rápido) está usando o Uint8Array . Por exemplo, criando um vetor preenchido com zero de elementos 1M:

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

Sou usuário de Linux e sempre trabalhei para mim, mas uma vez um amigo usando um Mac tinha alguns elementos diferentes de zero. Eu pensei que a sua máquina estava com defeito, mas ainda assim é a maneira mais segura que encontramos para consertá-lo:

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

Editado

Chrome 25.0.1364.160

  1. Frederik Gottlieb – 6,43
  2. Sam Barnum – 4,83
  3. Eli – 3,68
  4. Josué 2,91
  5. Mathew Crumley – 2,67
  6. bduran – 2,55
  7. Arroz Allen – 2,11
  8. kangax – 0,68
  9. Tj. Crowder – 0,67
  10. zertosh – ERRO

Firefox 20.0

  1. Arroz Allen – 1,85
  2. Josué – 1,82
  3. Mathew Crumley – 1,79
  4. bduran – 1,37
  5. Frederik Gottlieb – 0,67
  6. Sam Barnum – 0,63
  7. Eli – 0,59
  8. kagax – 0,13
  9. Tj. Crowder – 0,13
  10. zertosh – ERRO

Faltando o teste mais importante (pelo menos para mim): o do Node.js. Eu suspeito que perto do benchmark do Chrome.

Usando lodash ou sublinhado

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

Ou se você tiver um array existente e quiser um array do mesmo tamanho

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

Eu não tenho nada contra:

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

sugerido por Zertosh, mas em um novo ES6 array extensões permitem que você faça isso nativamente com o método de fill . Agora borda IE, Chrome e FF suporta, mas verifique a tabela de compatibilidade

new Array(3).fill(0) lhe dará [0, 0, 0] . Você pode preencher o array com qualquer valor como o new Array(5).fill('abc') (até objects e outros arrays).

Além disso, você pode modificar os arrays anteriores com preenchimento:

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

que te dá: [1, 2, 3, 9, 9, 6]

A partir do ECMAScript2016 , há uma opção clara para grandes matrizes.

Como essa resposta ainda aparece perto do topo das pesquisas do Google, aqui está uma resposta para 2017.

Aqui está um jsbench atual com algumas dezenas de methods populares, incluindo muitos propostos até agora sobre esta questão. Se você encontrar um método melhor, por favor adicione, bifurque e compartilhe.

Quero observar que não existe uma maneira realmente eficiente de criar um array com comprimento zero arbitrário. Você pode otimizar a velocidade, ou para clareza e facilidade de manutenção – pode ser considerada a escolha mais eficiente, dependendo das necessidades do projeto.

Ao otimizar a velocidade, você deseja: criar a matriz usando a syntax literal; defina o comprimento, inicialize a variável de iteração e itere o array usando um loop while. Aqui está um exemplo.

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

Solução ES6:

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

Que tal o new Array(51).join('0').split('') ?

Não vi este método em respostas, então aqui está:

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

Como resultado, você obterá uma matriz com valor zero de comprimento de 200:

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

Não tenho certeza sobre o desempenho desse código, mas não deve ser um problema se você usá-lo para matrizes relativamente pequenas.

Minha function mais rápida seria:

 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 

Usar o push e shift nativos para adicionar itens à matriz é muito mais rápido (cerca de 10 vezes) do que declarar o escopo da matriz e fazer referência a cada item para definir seu valor.

fyi: Eu sempre obtenho tempos mais rápidos com o primeiro loop, que é a contagem regressiva, ao executar isso no firebug (extensão 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 

Estou interessado em saber o que TJ Crowder faz disso? 🙂

Eu estava testando a excelente resposta de TJ Crowder e desenvolvi uma fusão recursiva baseada na solução de concatuação que supera qualquer um dos testes dele no Chrome (eu não testei outros navegadores).

 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; }, 

chame o método com makeRec(29) .

Esta versão concat é muito mais rápida nos meus testes no Chrome (2013-03-21). Cerca de 200ms para 10.000.000 elementos vs 675 para o init direto.

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

Bônus: se você quiser preencher sua matriz com Strings, essa é uma maneira concisa de fazer isso (não tão rápido quanto a concat ):

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

Eu sabia que tinha esse protótipo em algum lugar 🙂

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

Editar: testes

Em resposta a Joshua e outros methods, eu fiz o meu próprio benchmarking, e estou vendo resultados completamente diferentes daqueles reportados.

Aqui está o que testei:

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

Resultados:

 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 

Então, pelo meu cálculo, o push é geralmente mais lento, mas funciona melhor com arrays mais longos em FF, mas pior no IE, que é uma droga em geral (quel surprise).

It might be worth pointing out, that Array.prototype.fill had been added as part of the ECMAScript 6 (Harmony) proposal . I would rather go with the polyfill written below, before considering other options mentioned on the thread.

 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; }; } 
 var str = "0000000...0000"; var arr = str.split(""); 

usage in expressions: arr[i]*1;

EDIT: if arr supposed to be used in integer expressions, then please don’t mind the char value of ‘0’. You just use it as follows: a = a * arr[i] (assuming a has integer value).

Shortest for loop code

 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; 

Safe var version

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

The fastest way to do that is with forEach =)

(we keep backward compatibility for IE < 9)

 var fillArray = Array.prototype.forEach ? function(arr, n) { arr.forEach(function(_, index) { arr[index] = n; }); return arr; } : function(arr, n) { var len = arr.length; arr.length = 0; while(len--) arr.push(n); return arr; }; // test fillArray([1,2,3], 'X'); // => ['X', 'X', 'X'] 

There’s always the phpjs solution, which you can find here:

http://phpjs.org/functions/array_fill/

I can’t speak for the project (creating a library of javascript functions that mirrors the greater functionality of php) as a whole, but the few functions that I’ve personally pulled from there have worked like a champ.

I just use :

 var arr = [10]; for (var i=0; i<=arr.length;arr[i] = i, i++); 

Anonymous function:

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

A bit shorter with for-loop:

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

Works with any Object , just change what’s inside this.push() .

You can even save the function:

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

Call it using:

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

Adding elements to an already existing array:

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

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