Qual é a maneira mais eficiente de criar uma matriz arbitrária de tamanho zero preenchida em JavaScript?
let i = 0; Array.from(Array(10), ()=>i++);
Qual é a maneira mais eficiente de criar uma matriz arbitrária de tamanho zero preenchida em JavaScript?
let i = 0; Array.from(Array(10), ()=>i++);
Respostas:
O ES6 apresenta 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 auto-descritivo.
Ainda não está no IE ( verifique a compatibilidade ), mas há um polyfill disponível .
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 amarrado
arr = 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 a arr = 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ão new Array()
parece ter um desempenho melhor. Mas ..
arr.fill(0)
... tudo meio que muda. Agora, o uso new 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'));
vs new 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:
Array.apply(null, Array(5)).map(Number.prototype.valueOf,0);
// [0, 0, 0, 0, 0]
Se eu quero 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 sequência), poderá:
new Array(5+1).join('0').split('').map(parseFloat)
// [0, 0, 0, 0, 0]
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 }
- tente console.dir( Array(5) )
. Note que ele não tem quaisquer propriedades 0
, 1
, 2
, etc. Mas quando você apply
que até o Array
construtor, é como dizer Array(undefined, undefined, undefined, undefined, undefined)
. E você recebe um objeto que meio que se parece { length: 5, 0: undefined, 1: undefined...}
. map
funciona nas propriedades 0
, 1
etc. é por isso que seu exemplo não funciona, mas quando você o usa apply
.
.apply
é realmente o que você deseja this
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. Gosto null
porque é barato, você provavelmente não quer usar {}
ou []
porque instancia um objeto sem motivo.
Aqui está outra maneira de fazer isso usando o ES6 que ninguém mencionou até agora:
> Array.from(Array(3), () => 0)
< [0, 0, 0]
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 valor 0
.
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:
> Array.from(Array(10), () => Math.floor(10 * Math.random()))
< [3, 6, 8, 1, 9, 3, 0, 6, 7, 1]
É mais conciso (e elegante) do que o equivalente:
const numbers = Array(10);
for (let i = 0; i < numbers.length; i++) {
numbers[i] = Math.round(10 * Math.random());
}
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:
> Array.from(Array(10), (d, i) => i)
< [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
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:
> "?".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 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:
> "?".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
truque definitivamente não é procurado na produção, Array.from()
é perfeitamente bem :-)
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)
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
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 matrizes
O código abaixo apresenta soluções usadas em medições
Resultados de exemplo para o Chrome
let a=[]; for(i=n;i--;) a.push(0);
- mas é 4x mais lento que fill(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 é
a = new Array(10).fill(0);
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.
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], como Int32Array
essas. Observe também que o ES2015 adiciona um fill
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:
function newFilledArray(len, val) {
var rv = new Array(len);
while (--len >= 0) {
rv[len] = val;
}
return rv;
}
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 ):
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Zero Init Test Page</title>
<style type='text/css'>
body {
font-family: sans-serif;
}
#log p {
margin: 0;
padding: 0;
}
.error {
color: red;
}
.winner {
color: green;
font-weight: bold;
}
</style>
<script type='text/javascript' src='prototype-1.6.0.3.js'></script>
<script type='text/javascript'>
var testdefs = {
'downpre': {
total: 0,
desc: "Count down, pre-decrement",
func: makeWithCountDownPre
},
'downpost': {
total: 0,
desc: "Count down, post-decrement",
func: makeWithCountDownPost
},
'up': {
total: 0,
desc: "Count up (normal)",
func: makeWithCountUp
},
'downandup': {
total: 0,
desc: "Count down (for loop) and up (for filling)",
func: makeWithCountDownArrayUp
},
'concat': {
total: 0,
desc: "Concat",
func: makeWithConcat
}
};
document.observe('dom:loaded', function() {
var markup, defname;
markup = "";
for (defname in testdefs) {
markup +=
"<div><input type='checkbox' id='chk_" + defname + "' checked>" +
"<label for='chk_" + defname + "'>" + testdefs[defname].desc + "</label></div>";
}
$('checkboxes').update(markup);
$('btnTest').observe('click', btnTestClick);
});
function epoch() {
return (new Date()).getTime();
}
function btnTestClick() {
// Clear log
$('log').update('Testing...');
// Show running
$('btnTest').disabled = true;
// Run after a pause while the browser updates display
btnTestClickPart2.defer();
}
function btnTestClickPart2() {
try {
runTests();
}
catch (e) {
log("Exception: " + e);
}
// Re-enable the button; we don't yheidl
$('btnTest').disabled = false;
}
function runTests() {
var start, time, counter, length, defname, def, results, a, invalid, lowest, s;
// Get loops and length
s = $F('txtLoops');
runcount = parseInt(s);
if (isNaN(runcount) || runcount <= 0) {
log("Invalid loops value '" + s + "'");
return;
}
s = $F('txtLength');
length = parseInt(s);
if (isNaN(length) || length <= 0) {
log("Invalid length value '" + s + "'");
return;
}
// Clear log
$('log').update('');
// Do it
for (counter = 0; counter <= runcount; ++counter) {
for (defname in testdefs) {
def = testdefs[defname];
if ($('chk_' + defname).checked) {
start = epoch();
a = def.func(length);
time = epoch() - start;
if (counter == 0) {
// Don't count (warm up), but do check the algorithm works
invalid = validateResult(a, length);
if (invalid) {
log("<span class='error'>FAILURE</span> with def " + defname + ": " + invalid);
return;
}
}
else {
// Count this one
log("#" + counter + ": " + def.desc + ": " + time + "ms");
def.total += time;
}
}
}
}
for (defname in testdefs) {
def = testdefs[defname];
if ($('chk_' + defname).checked) {
def.avg = def.total / runcount;
if (typeof lowest != 'number' || lowest > def.avg) {
lowest = def.avg;
}
}
}
results =
"<p>Results:" +
"<br>Length: " + length +
"<br>Loops: " + runcount +
"</p>";
for (defname in testdefs) {
def = testdefs[defname];
if ($('chk_' + defname).checked) {
results += "<p" + (lowest == def.avg ? " class='winner'" : "") + ">" + def.desc + ", average time: " + def.avg + "ms</p>";
}
}
results += "<hr>";
$('log').insert({top: results});
}
function validateResult(a, length) {
var n;
if (a.length != length) {
return "Length is wrong";
}
for (n = length - 1; n >= 0; --n) {
if (a[n] != 0) {
return "Index " + n + " is not zero";
}
}
return undefined;
}
function makeWithCountDownPre(len) {
var a;
a = new Array(len);
while (--len >= 0) {
a[len] = 0;
}
return a;
}
function makeWithCountDownPost(len) {
var a;
a = new Array(len);
while (len-- > 0) {
a[len] = 0;
}
return a;
}
function makeWithCountUp(len) {
var a, i;
a = new Array(len);
for (i = 0; i < len; ++i) {
a[i] = 0;
}
return a;
}
function makeWithCountDownArrayUp(len) {
var a, i;
a = new Array(len);
i = 0;
while (--len >= 0) {
a[i++] = 0;
}
return a;
}
function makeWithConcat(len) {
var a, rem, currlen;
if (len == 0) {
return [];
}
a = [0];
currlen = 1;
while (currlen < len) {
rem = len - currlen;
if (rem < currlen) {
a = a.concat(a.slice(0, rem));
}
else {
a = a.concat(a);
}
currlen = a.length;
}
return a;
}
function log(msg) {
$('log').appendChild(new Element('p').update(msg));
}
</script>
</head>
<body><div>
<label for='txtLength'>Length:</label><input type='text' id='txtLength' value='10000'>
<br><label for='txtLoops'>Loops:</label><input type='text' id='txtLoops' value='10'>
<div id='checkboxes'></div>
<br><input type='button' id='btnTest' value='Test'>
<hr>
<div id='log'></div>
</div></body>
</html>
Por padrão Uint8Array
, Uint16Array
e as Uint32Array
classes mantêm zeros como seus valores, para que você não precise de técnicas complexas de preenchimento, basta:
var ary = new Uint8Array(10);
todos os elementos da matriz ary
serão zeros por padrão.
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 de 0
. 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:
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]
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']
Note-se que while
geralmente é mais eficiente que for-in
, forEach
etc.
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 usar arr.push(value)
. É chato, porque eu prefiro o seu método.
usando notação de objeto
var x = [];
zero preenchido? 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 observação, se você modificar o protótipo do Array, ambos
var x = new Array();
e
var y = [];
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.
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.
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;
}
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 o array.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 um for
laço ... mas init não é mais necessária, de modo a while
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:
var zero = newFilledArray(maxLength, 0);
Agora divida essa matriz sempre que precisar de uma matriz preenchida com zero de comprimento requiredLength < maxLength :
zero.slice(0, requiredLength);
Eu estava criando matrizes com zero preenchimento milhares de vezes durante a execução do meu código, isso acelerou tremendamente o processo.
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 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 compatibilidade
new Array(3).fill(0)
vai te dar [0, 0, 0]
. Você pode preencher a matriz com qualquer valor como new Array(5).fill('abc')
(mesmo objetos e outras matrizes).
Além disso, você pode modificar as matrizes anteriores com preenchimento:
arr = [1, 2, 3, 4, 5, 6]
arr.fill(9, 3, 5) # what to fill, start, end
o que lhe dá: [1, 2, 3, 9, 9, 6]
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:
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 sua máquina estava com defeito, mas ainda está aqui a maneira mais segura que encontramos para corrigi-la:
var zeroFilled = [].slice.apply(new Uint8Array(new Array(1000000))
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.
Usando lodash ou sublinhado
_.range(0, length - 1, 0);
Ou se você possui uma matriz existente e deseja uma matriz do mesmo comprimento
array.map(_.constant(0));
_.range(0, length, 0)
, acredito. O Lodash é exclusivo do valor final
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.
const arr = [];
arr.length = 120000;
let i = 0;
while (i < 120000) {
arr[i] = 0;
i++;
}
Outra implementação possível seria:
(arr = []).length = n;
let i = 0;
while (i < n) {
arr[i] = 0;
i++;
}
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
const arr = Array(n).fill(0);
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. Use const
para variáveis que não serão redefinidas e let
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.
Para criar uma nova matriz totalmente nova
new Array(arrayLength).fill(0);
Para adicionar alguns valores no final de uma matriz existente
[...existingArray, ...new Array(numberOfElementsToAdd).fill(0)]
//**To create an all new Array**
console.log(new Array(5).fill(0));
//**To add some values at the end of an existing Array**
let existingArray = [1,2,3]
console.log([...existingArray, ...new Array(5).fill(0)]);
Não viu esse método nas respostas, então aqui está:
"0".repeat( 200 ).split("").map( parseFloat )
Como resultado, você obterá uma matriz de comprimento zero com valor zero:
[ 0, 0, 0, 0, ... 0 ]
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.
const arr = Array.from({ length: 10 }).fill(0)
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.
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ê deseja preencher sua matriz com Strings, esta é uma maneira concisa de fazê-lo (não tão rápido quanto se concat
fosse):
function filledArrayString(len, value) {
return new Array(len+1).join(value).split('');
}
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).
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)
.
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.
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;
};
}
Menor para código de loop
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;
Versão var segura
var a=[],i=0;for(;i<100;)a[i++]=0;
edit:
for(var i=100,a=[];i--;)a[i]=0;
n
, isso seria mais curto:for(var a=[];n--;a[n]=0);
let filled = [];
filled.length = 10;
filled.fill(0);
console.log(filled);
Minha função 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 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).
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? :-)
while (len--)
.. levei meus tempos de processamento de cerca de 60ms para cerca de 54ms
Eu sabia que tinha esse proto'd 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 métodos, executei meu próprio benchmarking e estou vendo resultados completamente diferentes dos relatados.
Aqui está o que eu 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 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).
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 a this.length
verificação. Isso reduzirá uma matriz já existente, se necessário, quando a init
reializar para um comprimento diferente n
.
Função anônima:
(function(n) { while(n-- && this.push(0)); return this; }).call([], 5);
// => [0, 0, 0, 0, 0]
Um pouco mais curto com o loop for:
(function(n) { for(;n--;this.push(0)); return this; }).call([], 5);
// => [0, 0, 0, 0, 0]
Funciona com qualquer um Object
, basta alterar o que está dentro this.push()
.
Você pode até salvar a função:
function fill(size, content) {
for(;size--;this.push(content));
return this;
}
Ligue usando:
var helloArray = fill.call([], 5, 'hello');
// => ['hello', 'hello', 'hello', 'hello', 'hello']
Adicionando elementos a uma matriz já existente:
var helloWorldArray = fill.call(helloArray, 5, 'world');
// => ['hello', 'hello', 'hello', 'hello', 'hello', 'world', 'world', 'world', 'world', 'world']
Desempenho: http://jsperf.com/zero-filled-array-creation/25