Qual é a maneira mais eficiente de criar uma matriz arbitrária de tamanho zero preenchida em JavaScript?
javascript
arrays
dil
fonte
fonte
let i = 0; Array.from(Array(10), ()=>i++);
Respostas:
O ES6 apresenta
Array.prototype.fill
. Pode ser usado assim:Não tenho certeza se é rápido, mas eu gosto porque é curto e auto-descritivo.
Ainda não está no IE ( verifique a compatibilidade ), mas há um polyfill disponível .
fonte
new Array(len)
é dolorosamente lento.(arr = []).length = len; arr.fill(0);
é sobre a solução mais rápida eu já vi em qualquer lugar ... ou pelo menos amarradoarr = Array(n)
e(arr = []).length = n
se comportam de forma idêntica, de acordo com as especificações. Em algumas implementações, pode-se ser mais rápido, mas não acho que haja uma grande diferença.(arr = []).length = 1000;
contra aarr = new Array(1000);
velocidade, teste-o no Chrome e no FF ...new
é muito lento. Agora, para comprimentos de matriz menores ... digamos <50 ou há muitos ... entãonew Array()
parece ter um desempenho melhor. Mas ..arr.fill(0)
... tudo meio que muda. Agora, o usonew Array()
é mais rápido na maioria dos casos, exceto quando você atinge tamanhos de matriz> 100000 ... Então você pode começar a ver a velocidade aumentar novamente. Mas se você não precisar preenchê-lo com zeros e puder usar o padrão padrão de matrizes vazias. Então(arr = []).length = x
fica louco rápido nos meus casos de teste na maioria das vezes.new Array(5).forEach(val => console.log('hi'));
vsnew Array(5).fill(undefined).forEach(val => console.log('hi'));
.Embora esse seja um tópico antigo, eu queria adicionar meus 2 centavos. Não sei o quão lento / rápido isso é, mas é rápido. Aqui está o que eu faço:
Se eu quiser preencher previamente um número:
Se eu quero preencher previamente com uma string:
Outras respostas sugeriram:
mas se você quiser 0 (o número) e não "0" (zero dentro de uma sequência), poderá:
fonte
Array.apply(null, new Array(5)).map(...)
? Porque simplesmente fazendo (new Array (5)) mapear (...) não vai funcionar como a especificação diz.new
) Quando vocêArray(5)
cria um objeto que se parece com isso:{ length: 5, __proto__: Array.prototype }
- tenteconsole.dir( Array(5) )
. Note que ele não tem quaisquer propriedades0
,1
,2
, etc. Mas quando vocêapply
que até oArray
construtor, é como dizerArray(undefined, undefined, undefined, undefined, undefined)
. E você recebe um objeto que meio que se parece{ length: 5, 0: undefined, 1: undefined...}
.map
funciona nas propriedades0
,1
etc. é por isso que seu exemplo não funciona, mas quando você o usaapply
..apply
é realmente o que você desejathis
que seja. Para esses propósitos,this
isso não importa - nós realmente nos preocupamos apenas com o "recurso" de espalhamento de parâmetros.apply
- para que possa ter qualquer valor. Gostonull
porque é barato, você provavelmente não quer usar{}
ou[]
porque instancia um objeto sem motivo.Maneira elegante de preencher uma matriz com valores pré-computados
Aqui está outra maneira de fazer isso usando o ES6 que ninguém mencionou até agora:
Ele funciona passando uma função 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 função lambda mapeia cada uma delas para o valor0
.Embora
Array(len).fill(0)
seja mais curto, não funcionará se você precisar preencher a matriz fazendo primeiro alguns cálculos (sei que a pergunta não foi solicitada, mas muitas pessoas acabam aqui procurando por isso) .Por exemplo, se você precisar de uma matriz com 10 números aleatórios:
É mais conciso (e elegante) do que o equivalente:
Este método também pode ser usado para gerar sequências de números, aproveitando o parâmetro index fornecido no retorno de chamada:
Resposta bônus: preencha uma matriz usando String
repeat()
Como esta resposta está recebendo muita atenção, eu também queria mostrar esse truque legal. Embora não seja tão útil quanto minha resposta principal, apresentarei o
repeat()
método String ainda não muito conhecido, mas muito útil . Aqui está o truque:Legal né?
repeat()
é um método muito útil para criar uma sequência que é a repetição da sequência original um certo número de vezes. Depois disso,split()
cria uma matriz para nós, que émap()
pedida para os valores que queremos. Dividindo em etapas:fonte
repeat
truque definitivamente não é procurado na produção,Array.from()
é perfeitamente bem :-)Em resumo
Solução mais rápida
let a = new Array(n); for (let i=0; i<n; ++i) a[i] = 0;
Solução mais curta (prática) (3x mais lenta para matrizes pequenas, ligeiramente mais lenta para grandes (mais lenta no Firefox))
Array(n).fill(0)
Detalhes
Hoje 2020.06.09 realizo testes no macOS High Sierra 10.13.6 nos navegadores Chrome 83.0, Firefox 77.0 e Safari 13.1. Testo soluções escolhidas para dois casos de teste
Conclusões
new Array(n)+for
(N) é a solução mais rápida para matrizes pequenas e grandes (exceto o Chrome, mas ainda assim muito rápida) e é recomendada como solução rápida entre navegadoresnew Float32Array(n)
(I) retorna um array não típico (por exemplo, você não pode acessápush(..)
-lo), por isso não comparo seus resultados com outras soluções - no entanto, essa solução é 10 a 20 vezes mais rápida que outras soluções para grandes matrizes em todos os navegadoresfor
(L, M, N, O) são rápidas para pequenas matrizesfill
(B, C) são rápidas no Chrome e Safari, mas surpreendentemente mais lentas no Firefox para grandes matrizes. Eles são médios rápido para pequenas matrizesArray.apply
(P) gera erro para grandes matrizesMostrar snippet de código
Código e exemplo
O código abaixo apresenta soluções usadas em medições
Mostrar snippet de código
Resultados de exemplo para o Chrome
fonte
let a=[]; for(i=n;i--;) a.push(0);
- mas é 4x mais lento quefill(0)
-, por isso não atualizarei a imagem nesse caso.O método de preenchimento ES 6 já mencionado cuida bem disso. A maioria dos navegadores de desktop modernos já suporta os métodos de protótipo de matriz exigidos atualmente (Chromium, FF, Edge e Safari) [ 1 ]. Você pode procurar detalhes no MDN . Um exemplo simples de uso é
Dado o suporte atual ao navegador, você deve ser cauteloso ao usá-lo, a menos que tenha certeza de que seu público-alvo usa navegadores modernos de área de trabalho.
fonte
a = Array(10).fill(null).map(() => { return []; });
a = Array(10).fill(0).map( _ => [] );
Nota adicionada em agosto de 2013, atualizada em fevereiro de 2015: A resposta abaixo de 2009 refere-se ao
Array
tipo genérico do JavaScript . Ele não está relacionado às novas matrizes digitadas definidas no ES2015 [e disponíveis agora em muitos navegadores], comoInt32Array
essas. Observe também que o ES2015 adiciona umfill
método às matrizes e às matrizes digitadas , que provavelmente é 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 Chrome, em particular, tenta usar uma matriz de memória contígua altamente eficiente, se achar que pode, mudando para a matriz baseada em objetos somente quando necessário.
Na maioria dos idiomas, ele seria pré-alocado e preenchido com zero, assim:
Porém , matrizes JavaScript não são realmente matrizes , são mapas de chave / valor, como todos os outros objetos JavaScript, portanto, não há "pré-alocação" a fazer (definir o comprimento não aloca muitos slots para preencher), nem existe alguma razão para acreditar que o benefício da contagem regressiva a zero (que é apenas para fazer a comparação rápida no loop) não seja compensado pela adição das chaves na ordem inversa, quando a implementação pode ter otimizado o manuseio das chaves relacionado a matrizes na teoria, você geralmente as faz em ordem.
De fato, Matthew Crumley apontou que a contagem decrescente é muito mais lenta no Firefox do que a contagem regressiva, um resultado que posso confirmar - é a parte da matriz (o loop de zero a zero ainda é mais rápido do que o limite de um var). Aparentemente, adicionar os elementos à matriz na ordem inversa é uma operação lenta no Firefox. De fato, os resultados variam bastante de acordo com a implementação do JavaScript (o que não é tão surpreendente). Aqui está uma página de teste rápida e suja (abaixo) para implementações do navegador (muito sujas, não produzem durante os testes, por isso, fornece um feedback mínimo e afeta os prazos de script). Eu recomendo atualizar entre os testes; FF (pelo menos) diminui a velocidade em testes repetidos, se você não o fizer.
A versão bastante complicada que usa o Array # concat é mais rápida que uma inicialização direta no FF, entre algo entre 1.000 e 2.000 arrays de elemento. 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 ):
fonte
Por padrão
Uint8Array
,Uint16Array
e asUint32Array
classes mantêm zeros como seus valores, para que você não precise de técnicas complexas de preenchimento, basta:todos os elementos da matriz
ary
serão zeros por padrão.fonte
Array.isArray(ary)
éfalse
. O comprimento também é só de leitura para que você não pode empurrar novos itens a ele como comary.push
0
como seu valor padrão.Array.from(new Uint8Array(10))
fornecerá uma matriz normal.Array(n).fill(0)
no Chrome se o que você realmente precisa é de uma matriz JS. Se você pode usar um TypedArray, isso é muito mais rápido que.fill(0)
, especialmente se você pode usar o valor inicializador padrão de0
. Não parece haver um construtor que tenha um valor e comprimento de preenchimento, como o C ++std::vector
. Parece para qualquer valor diferente de zero você tem que construir um TypedArray zerada e , em seguida, preenchê-lo. : /Se você usa o ES6, pode usar o Array.from () assim:
Tem o mesmo resultado que
Porque
fonte
Note-se que
while
geralmente é mais eficiente quefor-in
,forEach
etc.fonte
i
variável local não é estranha?length
é passado por valor, portanto você deve poder diminuí-lo diretamente.arr[i] = value
). É muito mais rápido percorrer do começo ao fim e usararr.push(value)
. É chato, porque eu prefiro o seu método.usando notação de objeto
zero preenchido? gostar...
preenchido com 'indefinido' ...
notação obj com zeros
Como observação, se você modificar o protótipo do Array, ambos
e
terá essas modificações de protótipo
De qualquer forma, eu não me preocuparia muito com a eficiência ou a velocidade dessa operação; há muitas outras coisas que você provavelmente fará muito mais dispendiosas e caras do que instanciar uma matriz de comprimento arbitrário contendo zeros.
fonte
null
s nesta matrizvar x = new Array(7);
new Array(7)
se não criar uma matriz "cheio de undefined". Ele cria uma matriz vazia com o comprimento 7. #(new Array(10)).fill(0)
.Testei todas as combinações de pré-alocação / não alocação, contagem para cima / baixo e por / enquanto loops no IE 6/7/8, Firefox 3.5, Chrome 3.5 e Chrome.
As funções abaixo foram 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 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 também a incluo para referência.
ou
fonte
var array = []
declaração na primeira parte do loop for, separada por apenas uma vírgula.length
valor já fornecido, para que não mude constantemente. Trouxe uma matriz de 1 milhão de zeros de 40ms a 8 na minha máquina.for (i = 0, array = []; i < length; ++i) array[i] = val;
.. Menos blocos? ... de qualquer forma, também ... se eu definir oarray.length
tamanho da nova matriz para o comprimento ... parece que recebo outro aumento de velocidade de 10% a 15% no FF ... no Chrome, parece dobrar a velocidade ->var i, array = []; array.length = length; while(i < length) array[i++] = val;
(ainda mais rápido foi se eu deixei-o como umfor
laço ... mas init não é mais necessária, de modo awhile
fica bem mais rápido com esta versão)Se você precisar criar muitas matrizes preenchidas com zero de diferentes comprimentos durante a execução do seu código, a maneira mais rápida que encontrei para isso é criar uma matriz zero uma vez , usando um dos métodos mencionados neste tópico, de um comprimento você sabe que nunca será excedido e, em seguida, corte essa matriz conforme necessário.
Por exemplo (usando a função da resposta escolhida acima para inicializar a matriz), crie uma matriz de comprimento maxLength preenchida com zero , como uma variável visível para o código que precisa de matrizes zero:
Agora divida essa matriz sempre que precisar de uma matriz preenchida com zero de comprimento requiredLength < maxLength :
Eu estava criando matrizes com zero preenchimento milhares de vezes durante a execução do meu código, isso acelerou tremendamente o processo.
fonte
fonte
new Array(size+1).join("x").split("x").map(function() { return 0; })
para obter números reaisnew Array(size+1).join('0').split('').map(Number)
Eu não tenho nada contra:
sugerido por Zertosh, mas em uma nova extensão de matriz do ES6, você pode fazer isso nativamente com o
fill
método Agora no IE edge, o Chrome e o FF o suportam, mas verifique a tabela de compatibilidadenew Array(3).fill(0)
vai te dar[0, 0, 0]
. Você pode preencher a matriz com qualquer valor comonew Array(5).fill('abc')
(mesmo objetos e outras matrizes).Além disso, você pode modificar as matrizes anteriores com preenchimento:
o que lhe dá:
[1, 2, 3, 9, 9, 6]
fonte
O jeito que eu costumo fazer isso (e é incrivelmente rápido) está usando
Uint8Array
. Por exemplo, criando um vetor preenchido com zero de 1 milhão de elementos: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 sua máquina estava com defeito, mas ainda está aqui a maneira mais segura que encontramos para corrigi-la:
Editado
Chrome 25.0.1364.160
Firefox 20.0
Faltando o teste mais importante (pelo menos para mim): o Node.js. Suspeito que seja próximo do benchmark do Chrome.
fonte
Usando lodash ou sublinhado
Ou se você possui uma matriz existente e deseja uma matriz do mesmo comprimento
fonte
_.range(0, length, 0)
, acredito. O Lodash é exclusivo do valor finalSolução ES6:
fonte
Desde o ECMAScript2016 , existe uma opção clara para matrizes grandes.
Como essa resposta ainda aparece na parte superior das pesquisas do Google, aqui está uma resposta para 2017.
Aqui está um jsbench atual com algumas dezenas de métodos populares, incluindo muitos propostos até agora sobre essa questão. Se você encontrar um método melhor, adicione, bifurque e compartilhe.
Quero observar que não existe uma maneira mais eficiente de criar uma matriz arbitrária de comprimento zero. Você pode otimizar a velocidade ou a clareza e a manutenção - ou pode ser considerada a opção mais eficiente, dependendo das necessidades do projeto.
Ao otimizar a velocidade, você deseja: criar a matriz usando sintaxe literal; defina o comprimento, inicialize a variável de iteração e itere através da matriz usando um loop while. Aqui está um exemplo.
Outra implementação possível seria:
Mas eu desencorajo fortemente o uso deste segundo implante na prática, pois é menos claro e não permite que você mantenha o escopo do bloco na variável de matriz.
Elas são significativamente mais rápidas que o preenchimento com um loop for e cerca de 90% mais rápidas que o método padrão de
Mas esse método de preenchimento ainda é a opção mais eficiente para matrizes menores devido à sua clareza, concisão e facilidade de manutenção. A diferença de desempenho provavelmente não o matará, a menos que você esteja criando muitas matrizes com comprimentos da ordem de milhares ou mais.
Algumas outras notas importantes. A maioria dos guias de estilo recomenda que você não use mais
var
sem um motivo muito especial ao usar o ES6 ou posterior. Useconst
para variáveis que não serão redefinidas elet
para variáveis que serão redefinidas . O MDN e o Guia de Estilo do Airbnb são ótimos lugares para obter mais informações sobre as melhores práticas. As perguntas não eram sobre sintaxe, mas é importante que as pessoas novas na JS conheçam esses novos padrões ao pesquisar essas resmas de respostas antigas e novas.fonte
Para criar uma nova matriz totalmente nova
Para adicionar alguns valores no final de uma matriz existente
[...existingArray, ...new Array(numberOfElementsToAdd).fill(0)]
Exemplo
fonte
Não viu esse método nas respostas, então aqui está:
Como resultado, você obterá uma matriz de comprimento zero com valor zero:
Não tenho certeza sobre o desempenho desse código, mas não deve ser um problema se você o usar para matrizes relativamente pequenas.
fonte
fonte
Esta
concat
versão é muito mais rápida nos meus testes no Chrome (21/03/2013). Cerca de 200 ms para 10.000.000 de elementos vs 675 para init direto.Bônus: se você deseja preencher sua matriz com Strings, esta é uma maneira concisa de fazê-lo (não tão rápido quanto se
concat
fosse):fonte
Eu estava testando a ótima resposta de TJ Crowder, e surgiu com uma mesclagem recursiva baseada na solução concat que supera qualquer dos testes dele no Chrome (não testei outros navegadores).
chame o método com
makeRec(29)
.fonte
Que tal
new Array(51).join('0').split('')
?fonte
.map(function(a){return +a})
?Vale ressaltar que
Array.prototype.fill
foi adicionado como parte da proposta do ECMAScript 6 (Harmony) . Prefiro ir com o polyfill escrito abaixo, antes de considerar outras opções mencionadas no tópico.fonte
Menor para código de loop
Versão var segura
fonte
n
, isso seria mais curto:for(var a=[];n--;a[n]=0);
fonte
Minha função mais rápida seria:
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 referenciar cada item para definir seu valor.
fyi: Consigo sempre tempos mais rápidos com o primeiro loop, que está em contagem regressiva, ao executar isso no firebug (extensão do firefox).
Estou interessado em saber o que TJ Crowder faz disso? :-)
fonte
while (len--)
.. levei meus tempos de processamento de cerca de 60ms para cerca de 54msEu sabia que tinha esse proto'd em algum lugar :)
Editar: testes
Em resposta a Joshua e outros métodos, executei meu próprio benchmarking e estou vendo resultados completamente diferentes dos relatados.
Aqui está o que eu testei:
Resultados:
Então, pelo meu acerto de contas, é realmente mais lento, em geral, mas apresenta melhor desempenho com matrizes mais longas no FF, mas pior no IE, o que é péssimo em geral (surpresa surpreendente).
fonte
b = []...
) é 10-15% mais rápido que o primeiro, mas é 10 vezes mais lento que a resposta de Josué.else {this.length=n;}
após athis.length
verificação. Isso reduzirá uma matriz já existente, se necessário, quando ainit
reializar para um comprimento diferenten
.Função anônima:
Um pouco mais curto com o loop for:
Funciona com qualquer um
Object
, basta alterar o que está dentrothis.push()
.Você pode até salvar a função:
Ligue usando:
Adicionando elementos a uma matriz já existente:
Desempenho: http://jsperf.com/zero-filled-array-creation/25
fonte