A maneira mais eficiente de criar uma matriz JavaScript preenchida com zero?


602

Qual é a maneira mais eficiente de criar uma matriz arbitrária de tamanho zero preenchida em JavaScript?


7
Alguns dados reais sobre isso: jsperf.com/zeroarrayjs
Web_Designer 15/15

7
O preenchimento ES6 permite fazer isso de forma nativa.
Salvador Dali

1
arr = nova matriz (comprimento + 1) .joint (caractere) .split ('');
Jordan Stefanelli 29/11

4
ATUALIZAÇÃO 2016 : Outra referência personalizada aqui: jsfiddle.net/basickarl/md5z0Lqq
K - A toxicidade no SO está crescendo.

1
let i = 0; Array.from(Array(10), ()=>i++);
Bart Hoekstra

Respostas:


543

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 .


15
o preenchimento é rápido. 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
Pimp Trizkit

7
@PimpTrizkit arr = Array(n)e (arr = []).length = nse 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.
Oriol

2
Bem, comecei a testá-lo com matrizes multidimensionais e parecia acelerar bastante meus casos de teste. Tendo acabado de fazer mais alguns testes no FF41 e no Chrome45.0.2454.99 m. Sim, acho que realmente precisava de mais espaço para me explicar. A maioria dos meus testes foi parcial, eu admito. Mas verifique isso. Predefina um var e, usando apenas essa linha (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 ..
Pimp Trizkit

4
... vou admitir que perdi essa parte ... quando adiciono a segunda linha ao teste ... 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 = xfica louco rápido nos meus casos de teste na maioria das vezes.
Pimp Trizkit

4
Observe que, para iterar sobre a matriz (por exemplo, map ou forEach), os valores devem ser definidos , caso contrário, ignorará esses índices. Os valores que você define podem ser o que você quiser - até indefinidos. Exemplo: tentativa new Array(5).forEach(val => console.log('hi'));vs new Array(5).fill(undefined).forEach(val => console.log('hi'));.
ArneHugo

387

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]

6
Ótima resposta! Você pode, por favor, explicar o truque Array.apply(null, new Array(5)).map(...)? Porque simplesmente fazendo (new Array (5)) mapear (...) não vai funcionar como a especificação diz.
Dmitry Pashkevich

36
(btw, nós realmente não precisamos do 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ê applyque até o Arrayconstrutor, é como dizer Array(undefined, undefined, undefined, undefined, undefined). E você recebe um objeto que meio que se parece { length: 5, 0: undefined, 1: undefined...}. mapfunciona nas propriedades 0, 1etc. é por isso que seu exemplo não funciona, mas quando você o usa apply.
precisa saber é

4
O primeiro parâmetro para .applyé realmente o que você deseja thisque seja. Para esses propósitos, thisisso 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 nullporque é barato, você provavelmente não quer usar {}ou []porque instancia um objeto sem motivo.
precisa saber é o seguinte

2
Também inicializar com size + assign é muito mais rápido que push. Veja caso de teste jsperf.com/zero-fill-2d-array
Colin

2
e Array.apply (null, Array (5)). map (x => 0)? É um pouco mais curto!
Arch Linux Tux

97

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:

> 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 undefinede, 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]

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:

> "?".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]

Lotes de truques de salão no cargo, mas espero que ninguém que vai chegar código de produção :)
Eric Grange

Embora o repeattruque definitivamente não é procurado na produção, Array.from()é perfeitamente bem :-)
Lucio Paiva

Na verdade, Array.from () aqui está basicamente criando uma matriz, iterando através dela com map (), chamando uma função em cada item para criar uma nova matriz e descartando a primeira matriz ... Para uma pequena matriz, isso pode ser inócuo, para matrizes maiores, este é o tipo de padrão que resultam em pessoas chamando navegadores "devoradores de memória" :)
Eric Grange

Pessoas que lidam com matrizes grandes devem saber melhor que isso, definitivamente. Porém, para aplicativos comuns, a criação de uma matriz auxiliar de tamanho regular (até 10.000 elementos) que será descartada imediatamente é perfeitamente adequada (leva o mesmo tempo que se você evitasse a criação extra de matriz - testada com o Chrome mais recente). Para casos como esse, a legibilidade se torna mais importante do que pequenas otimizações de desempenho. Sobre o tempo O (n), é necessário se você precisar calcular algo diferente para cada elemento (o assunto principal da minha resposta). Essa discussão é muito interessante, feliz por você ter levantado!
Lucio Paiva

88

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

  • matriz pequena - com 10 elementos - você pode realizar o teste AQUI
  • grandes matrizes - com 1 milhão de elementos - você pode executar o teste AQUI

Conclusões

  • A solução baseada em 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 navegadores
  • A solução baseada em new 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 navegadores
  • soluções baseadas em for(L, M, N, O) são rápidas para pequenas matrizes
  • as soluções baseadas em fill(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 matrizes
  • solução baseada em Array.apply(P) gera erro para grandes matrizes

insira a descrição da imagem aqui

Código e exemplo

O código abaixo apresenta soluções usadas em medições

Resultados de exemplo para o Chrome

insira a descrição da imagem aqui


Acabei de executar alguns testes no Chrome 77 e um loop simples com push () é duas vezes mais rápido que o fill () ... Será que efeitos colaterais sutis do fill () impedem uma implementação mais eficiente?
Eric Grange

@EricGrange Eu atualizo a resposta - na parte inferior, atualizo o link para o benchamrk com a sua proposta: caso P 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.
Kamil Kiełczewski 9/10/19

2
Boas medidas. Análise: G é lento devido ao redimensionamento da matriz a cada iteração, e redimensionar significa fazer uma nova alocação de memória. A, B, M rápido, porque o dimensionamento é feito apenas uma vez. +1
Roland

@ gman, na verdade você está certo - eu não entendo como eu poderia ter esquecido - eu realizo novos testes e atualizo a resposta com mais detalhes - obrigado :)
Kamil Kiełczewski

63

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.


4
Se você preencher com um tipo de referência, será a mesma referência em todos eles. new Array (10) .Reabastecer (null) .map (() => []) seria uma maneira sucinta de contornar este problema (me queimou inicialmente haha)
John Culviner

4
ATUALIZAÇÃO 2016 : Esse método expulsa todo o resto da água, clique aqui para obter referências: jsfiddle.net/basickarl/md5z0Lqq
K - A toxicidade no SO está crescendo.

isso funcionará para matrizes. a = Array(10).fill(null).map(() => { return []; });
Andy Andy

2
@AndrewAnthonyGerst Terser:a = Array(10).fill(0).map( _ => [] );
Phrogz

50

Nota adicionada em agosto de 2013, atualizada em fevereiro de 2015: A resposta abaixo de 2009 refere-se ao Arraytipo genérico do JavaScript . Ele não está relacionado às novas matrizes digitadas definidas no ES2015 [e disponíveis agora em muitos navegadores], como Int32Arrayessas. Observe também que o ES2015 adiciona um fillmé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>

Não tenho certeza de que o preenchimento para trás importaria aqui, pois você está acessando apenas elementos (não os excluindo) e já pré-alocado. Estou errado?
Triptych

o ponto de preenchimento para trás não é particularmente a ver com a matriz, que tem a ver com a condição de fuga para o tempo - o Falsey 0 termina o loop de forma muito eficiente
annakata

(embora eu só notei este código não realmente fazer uso desse)
annakata

@annakata, você não pode usar isso aqui, porque 0 é um índice válido.
Triptych

@triptych: não é verdade, tudo que toma é a ordem correta - veja meu post
annakata

34

Por padrão Uint8Array, Uint16Arraye as Uint32Arrayclasses 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 aryserão zeros por padrão.


5
Isso é bom, mas lembre-se de que isso não pode ser tratado da mesma forma que uma matriz normal, por exemplo, 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
MusikAnimal

Todas as matrizes digitadas mantêm 0como seu valor padrão.
Jfunk #

2
@MusikAnimal, Array.from(new Uint8Array(10))fornecerá uma matriz normal.
Tomas Langkaas

@TomasLangkaas: Sim, mas outra resposta mostra que é cerca de 5x mais lenta que 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. : /
Peter Cordes

29

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]

23
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 whilegeralmente é mais eficiente que for-in, forEachetc.


3
A ivariável local não é estranha? lengthé passado por valor, portanto você deve poder diminuí-lo diretamente.
Sean Bright

3
Embora isso pareça ótimo no começo, infelizmente é muito lento atribuir valores em um ponto arbitrário em um arary (por exemplo 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.
22614 Nick Brunt

19

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.


5
Err ... não há nulls nesta matrizvar x = new Array(7);
#

5
Na verdade, a matriz não é preenchida com nada com o novo Array (n), nem mesmo 'indefinido', ele simplesmente define o valor do comprimento do array como n. Você pode verificar isso chamando (nova matriz (1)). ForEach (...). O forEach nunca executa, ao contrário de se você o chamar [indefinido].
JussiR

4
new Array(7)se não criar uma matriz "cheio de undefined". Ele cria uma matriz vazia com o comprimento 7. #
RobG

1
Você pode querer reconsiderar partes da sua resposta como o que @RobG está dizendo é crítica (se o que você estava dizendo é verdade, mapeamento teria sido muito mais fácil)
Abdo

1
Hoje em dia você poderia fazer (new Array(10)).fill(0).
Javier de la Rosa

18

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

1
Você também pode lançar a var array = []declaração na primeira parte do loop for, separada por apenas uma vírgula.
damianb

Gosto da sugestão de damianb, mas lembre-se de colocar a tarefa e a vírgula antes da incrementação! `for (var i = 0; i <comprimento; matriz [i] = val, i ++);
punstress

Faça o que todo mundo está perdendo para o seu segundo e defina o comprimento da matriz com o lengthvalor 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.
Jonathan Gray

Eu pareço ter um aumento de velocidade de 10 a 15% quando refatorar esta solução em um liner. for (i = 0, array = []; i < length; ++i) array[i] = val;.. Menos blocos? ... de qualquer forma, também ... se eu definir o array.lengthtamanho 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 forlaço ... mas init não é mais necessária, de modo a whilefica bem mais rápido com esta versão)
Pimp Trizkit

Também observarei isso nos meus testes. Em um número decente de meus casos de teste, a versão final acima parece ter um desempenho 3x a bem acima de 10x mais rápido ... Não sei ao certo por que ... (tamanhos de matriz diferentes testados entre chrome e FF)
Pimp Trizkit

13

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.


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

3
Você também pode usar new Array(size+1).join("x").split("x").map(function() { return 0; })para obter números reais
Yuval

6
@Yuval Or justnew Array(size+1).join('0').split('').map(Number)
Paul

11

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 fillmé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]


10

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

  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. Allen Rice - 2.11
  8. kangax - 0,68
  9. Tj. Crowder - 0,67
  10. zertosh - ERRO

Firefox 20.0

  1. Allen Rice - 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 Node.js. Suspeito que seja próximo do benchmark do Chrome.


Essa é a maneira mais eficiente para meus dedos e meus olhos. Mas é muito muito lento para o Chrome (de acordo com esse jsperf. 99% mais lento).
Orwellophile

1
Gostaria de saber se o problema no Mac do seu amigo estava relacionado a: stackoverflow.com/questions/39129200/… ou talvez Array.slice não estivesse manipulando o UInt8Array e vazando memória não inicializada? (um problema de segurança!).
precisa saber é

@robocat Boa captura! Se bem me lembro, estávamos usando o Node.js 0.6 ou 0.8. Pensamos em algum tipo de vazamento, mas não pudemos reproduzi-lo com a pilha de produção, então decidimos ignorá-lo.
durum

10

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

Que bom que você adicionou esta resposta, como eu uso o sublinhado, e sabia que havia algo para isso ... mas ainda não consegui encontrá-la. Eu só desejo que eu poderia criar matrizes de objetos usando este
PandaWood

@PandaWood _.range (0, length -1, 0) .map (Object.new), eu acho.
djechlin

Deveria ser _.range(0, length, 0), acredito. O Lodash é exclusivo do valor final
user4815162342

9

Solução ES6:

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

8

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 varsem um motivo muito especial ao usar o ES6 ou posterior. Use constpara variáveis ​​que não serão redefinidas e letpara 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.


8

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

Exemplo

//**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)]);


6

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.


5
Nem a mais rápida nem a mais curta, mas uma boa contribuição para a diversidade de soluções.
precisa saber é o seguinte


4

Esta concatversã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 concatfosse):

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

2
Ok, selvagem. Isso é MUITO mais rápido do que usar a nova matriz (len). MAS! Estou vendo no Chrome que as leituras subsequentes desses dados demoram substancialmente mais. Aqui estão alguns registros de data e hora para mostrar o que quero dizer: (Usando nova matriz (len)) 0.365: Fazendo a matriz 4.526: Executando a convolução 10.75: Convolução concluída (usando concat) 0.339: Fazendo a matriz 0.591: Executando a convolução // OMG, MUITO MAIS rápido 18.056: Convolução Completa
Brooks

4

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



4

Vale ressaltar que Array.prototype.fillfoi 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;
  };
}

4

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;

2
Dado que o comprimento é uma variável definida n, isso seria mais curto:for(var a=[];n--;a[n]=0);
Tomas Langkaas 8/16/16


3

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? :-)


Você pode torná-lo mais rápido, alterando-o para while (len--).. levei meus tempos de processamento de cerca de 60ms para cerca de 54ms
nickf

A resposta de Matthew Crumbly ainda supera isso (30ms)!
nickf

3

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


Acabei de testar isso: o segundo método ( b = []...) é 10-15% mais rápido que o primeiro, mas é 10 vezes mais lento que a resposta de Josué.
nickf

Eu sei que este é um post antigo . Mas talvez ainda seja do interesse de outras pessoas (como eu). Portanto, eu gostaria de sugerir uma adição à função prototype: inclua um else {this.length=n;}após a this.lengthverificação. Isso reduzirá uma matriz já existente, se necessário, quando a initreializar para um comprimento diferente n.
cars10m

2

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.