Como inicializar o tamanho de uma matriz em javascript?

A maioria dos tutoriais que eu li em matrizes em JavaScript (incluindo w3schools e devguru ) sugerem que você pode inicializar um array com um determinado comprimento passando um inteiro para o construtor Array usando o var test = new Array(4); syntax.

Depois de usar essa syntax generosamente nos meus arquivos js, eu corri um dos arquivos pelo jsLint , e isso me assustou:

Erro: Problema na linha 1 caractere 22: esperado ‘)’ e, em vez disso, viu ‘4’.
var test = new Matriz (4);
Problema na linha 1 caractere 23: esperado ‘;’ e em vez disso viu ‘)’.
var test = new Matriz (4);
Problema na linha 1 caractere 23: Esperado um identificador e, em vez disso, viu ‘)’.

Depois de ler a explicação do jsLint sobre seu comportamento , parece que o jsLint não gosta muito da new Array() syntax do new Array() , e prefere [] ao declarar matrizes.

Então eu tenho algumas perguntas. Primeiro porque? Estou correndo algum risco usando a new Array() syntax do new Array() ? Existem incompatibilidades de navegador que eu deveria estar ciente? E segundo, se eu mudar para a syntax de colchetes, existe alguma maneira de declarar um array e definir seu comprimento em uma linha, ou eu tenho que fazer algo assim:

 var test = []; test.length = 4; 

  1. Por que você quer inicializar o comprimento? Teoricamente, não há necessidade disso. Pode até resultar em um comportamento confuso, porque todos os testes que usam o length para descobrir se um array está vazio ou não relatarão que o array não está vazio.
    Alguns testes mostram que definir o comprimento inicial de matrizes grandes pode ser mais eficiente se o array for preenchido posteriormente, mas o ganho de desempenho (se houver) parecer diferir de navegador para navegador.

  2. jsLint não gosta de new Array() porque o construtor é ambíguo.

     new Array(4); 

    cria uma matriz vazia de comprimento 4. Mas

     new Array('4'); 

    cria uma matriz contendo o valor '4' .

Quanto ao seu comentário: No JS você não precisa inicializar o tamanho do array. Ela cresce dinamicamente. Você pode apenas armazenar o comprimento em alguma variável, por exemplo

 var data = []; var length = 5; // user defined length for(var i = 0; i < length; i++) { data.push(createSomeObject()); } 
  • Array(5) dá-lhe uma matriz com comprimento 5, mas sem valores, portanto, você não pode iterar sobre ele.

  • Array.apply(null, Array(5)).map(function () {}) lhe dá um array com tamanho 5 e indefinido como valores, agora ele pode ser iterado.

  • Array.apply(null, Array(5)).map(function (x, i) { return i; }) fornece uma matriz com tamanho 5 e valores 0,1,2,3,4.

  • Array(5).forEach(alert) não faz nada, Array.apply(null, Array(5)).forEach(alert) fornece 5 alertas

  • ES6 nos dá Array.from agora você também pode usar Array.from(Array(5)).forEach(alert)

  • Se você quiser inicializar com um certo valor, é bom saber …
    Array.from('abcde') , Array.from('x'.repeat(5))
    ou Array.from({length: 5}, (v, i) => i) // gives [0, 1, 2, 3, 4]

Com ES2015 .fill() agora você pode simplesmente fazer:

 //n is the size you want to initialize your array Array(n).fill(0) 

Que é muito mais conciso do que Array.apply(0, new Array(n)).map(i => 0)

Se não estivermos sendo pedantes, é possível descartar 0 na chamada .fill , mas isso pode falhar no Typescript.

O mais curto:

 [...Array(1000)] 

Isto irá inicializar a propriedade length para 4:

 var x = [,,,,]; 

O ES6 introduz o Array.from que permite criar um Array partir de qualquer object “array-like” ou iterables :

 Array.from({length: 10}, (x, i) => i); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

Nesse caso, {length: 10} representa a definição mínima de um object “tipo array” : um object vazio com apenas uma propriedade length definida.

Array.from permite que um segundo argumento seja Array.from sobre o array resultante.

Estou surpreso que não tenha sido sugerida uma solução funcional que permita definir o tamanho em uma linha. O seguinte é baseado no UnderscoreJS:

 var test = _.map(_.range(4), function () { return undefined; }); console.log(test.length); 

Por razões mencionadas acima, eu evitaria fazer isso a menos que eu quisesse inicializar a matriz para um valor específico. É interessante notar que existem outras bibliotecas que implementam o intervalo, incluindo Lo-dash e Lazy, que podem ter diferentes características de desempenho.

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

OU

 Array(6).fill(0); // [0, 0, 0, 0, 0, 0] 

OU

 Array(6).fill(null).map( (x,i) => i ); // [0, 1, 2, 3, 4, 5] 

( datilografado seguro )


Criando matrizes aninhadas

Ao criar um array 2D com o fill intuitivamente deve criar novas instâncias. Mas o que vai acontecer é que o mesmo array será armazenado como referência.

 var a = Array(3).fill([6]); // [[6], [6], [6]] a[ 0 ].push( 9 ); // [[6,9], [6,9], [6,9]] 

Solução

 var a = [...Array(3)].map(x=>[]); a[ 0 ].push( 4, 2 ); // [[4,2], [ ], [ ]] 

Então, uma matriz 3×2 será algo parecido com isto:

 [...Array(3)].map(x=>Array(2).fill(0)); // [ [0,0] , // [0,0] , // [0,0] ] 

(isso provavelmente foi melhor como um comentário, mas ficou muito longo)

Então, depois de ler isso, eu estava curioso para saber se a pré-alocação era realmente mais rápida, porque, em teoria, deveria ser. No entanto, este blog deu algumas dicas contra ele http://www.html5rocks.com/en/tutorials/speed/v8/ .

Então, ainda não tendo certeza, eu coloquei isso em teste. E, na verdade, parece ser mais lento.

 var time = Date.now(); var temp = []; for(var i=0;i<100000;i++){ temp[i]=i; } console.log(Date.now()-time); var time = Date.now(); var temp2 = new Array(100000); for(var i=0;i<100000;i++){ temp2[i] = i; } console.log(Date.now()-time); 

Este código produz o seguinte após algumas execuções casuais:

 $ node main.js 9 16 $ node main.js 8 14 $ node main.js 7 20 $ node main.js 9 14 $ node main.js 9 19 
 var arr=[]; arr[5]=0; alert("length="+arr.length); // gives 6 

Aqui está outra solução

 var arr = Array.apply( null, { length: 4 } ); arr; // [undefined, undefined, undefined, undefined] (in Chrome) arr.length; // 4 

O primeiro argumento de apply() é uma binding deste object, com a qual não nos importamos aqui, portanto, definimos como null .

Array.apply(..) está chamando a function Array(..) e distribuindo o valor do object { length: 3 } como seus argumentos.

Por favor, as pessoas não abandonem seus velhos hábitos ainda. Há uma grande diferença na complexidade entre alocar memory uma vez e depois trabalhar com as inputs naquela matriz (como antigamente) e alocá-la várias vezes à medida que uma matriz cresce (o que é inevitavelmente o que o sistema faz sob outros methods sugeridos) .

Nada disso é claro, até que você queira fazer algo legal com matrizes maiores. Então isso acontece.

Como ainda parece não haver nenhuma opção no JS para definir a capacidade inicial de um array, eu uso o seguinte …

 var newArrayWithSize = function(size) { this.standard = this.standard||[]; for (var add = size-this.standard.length; add>0; add--) { this.standard.push(undefined);// or whatever } return this.standard.slice(0,size); } 

Existem tradeoffs envolvidos:

  • Este método leva o tempo que os outros para a primeira chamada para a function, mas muito pouco tempo para chamadas posteriores (a menos que pedir uma matriz maior).
  • A matriz standard reserva permanentemente o máximo de espaço que a maior matriz solicitou.

Mas se isso se encheckbox com o que você está fazendo, pode haver um retorno. O tempo informal coloca

 for (var n=10000;n>0;n--) {var b = newArrayWithSize(10000);b[0]=0;} 

a muito veloz (cerca de 50ms para os 10000, dado que com n = 1000000 demorou cerca de 5 segundos), e

 for (var n=10000;n>0;n--) { var b = [];for (var add=10000;add>0;add--) { b.push(undefined); } } 

bem mais de um minuto (cerca de 90 seg para o 10000 no mesmo console cromado, ou cerca de 2000 vezes mais lento). Isso não será apenas a alocação, mas também os 10000 push, for loop, etc.

O construtor de matriz tem uma syntax ambígua , e o JSLint apenas prejudica seus sentimentos, afinal.

Além disso, seu código de exemplo está quebrado, a segunda instrução var gerará um SyntaxError . Você está definindo a length da propriedade do test matriz, portanto não há necessidade de outro var .

No que diz respeito às suas opções, array.length é o único “limpo”. A questão é, por que você precisa definir o tamanho em primeiro lugar? Tente refatorar seu código para se livrar dessa dependência.

O motivo pelo qual você não deve usar o new Array é demonstrado por este código:

 var Array = function () {}; var x = new Array(4); alert(x.length); // undefined... 

Algum outro código poderia mexer com a variável Array. Eu sei que é um pouco improvável que alguém escreva tal código, mas ainda assim …

Além disso, como Felix King disse, a interface é um pouco inconsistente, e pode levar a alguns erros muito difíceis de rastrear.

Se você quisesse um array com length = x, preenchido com undefined (como faria o new Array(x) ), você poderia fazer isso:

 var x = 4; var myArray = []; myArray[x - 1] = undefined; alert(myArray.length); // 4 

Como explicado acima, usar o new Array(size) é um pouco perigoso. Em vez de usá-lo diretamente, coloque-o dentro de uma “function de criador de matriz”. Você pode facilmente certificar-se de que esta function está livre de bugs e você evita o perigo de chamar diretamente o new Array(size) . Além disso, você pode fornecer um valor inicial padrão opcional. Esta function createArray faz exatamente isso:

 function createArray(size, defaultVal) { var arr = new Array(size); if (arguments.length == 2) { // optional default value for (int i = 0; i < size; ++i) { arr[i] = defaultVal; } } return arr; } 

Você pode definir o comprimento da matriz usando array.length = youValue

Então seria

 var myArray = []; myArray.length = yourValue;