Pesquisa de site

Como inicializar o comprimento de um array em JavaScript?


A maioria dos tutoriais que eu li sobre arrays em JavaScript (incluindo w3schools e devguru) sugere que você pode inicializar um array com um determinado comprimento passando um número inteiro para o construtor Array usando a sintaxe var test=new Array(4);.

Depois de usar essa sintaxe liberalmente nos meus arquivos js, eu executei um dos arquivos no jsLint, e ele pirou:

Erro: Problema na linha 1, caractere 22: Era esperado ')' e, em vez disso, encontrou '4'.
var test=new Array(4);
Problema na linha 1, caractere 23: Era esperado ';' e, em vez disso, encontrou ')'.
var test=new Array(4);
Problema na linha 1, caractere 23: Era esperado um identificador e, em vez disso, encontrou ')'.

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

Então, eu tenho algumas perguntas:

Primeiro, por quê? Estou correndo algum risco ao usar a sintaxe new Array() em vez disso? Existem incompatibilidades de navegador das quais eu deva estar ciente?

E segundo, se eu mudar para a sintaxe de colchetes, existe alguma maneira de declarar um array e definir seu comprimento tudo em uma linha, ou eu tenho que fazer algo assim:

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

Resposta #1

  • Array(5) te dá um array com comprimento 5, mas sem valores, portanto você não pode iterar sobre ele.

  • Array.apply(null, Array(5)).map(function() {}) te dá um array com comprimento 5 e valores indefinidos, agora ele pode ser iterado.

  • Array.apply(null, Array(5)).map(function (x, i) { return i; }) te dá um array com comprimento 5 e valores 0,1,2,3,4.

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

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

  • Se você quiser inicializar com um valor específico, estas são boas de saber...
    Array.from('abcde'), Array.from('x'.repeat(5))
    ou Array.from({length: 5}, (v, i) => i) /gera [0, 1, 2, 3, 4]

Resposta #2

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

// `n` is the size you want to initialize your array
// `0` is what the array will be filled with (can be any other value)
Array(n).fill(0)

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

É possível omitir o 0 em .fill() e executá-lo sem argumentos, o que preencherá o array com undefined. (No entanto, isso falhará no Typescript)

Resposta #3

  1. Por que você quer inicializar o tamanho? Teoricamente, não há necessidade disso. Isso pode até resultar em comportamento confuso, porque todos os testes que usam o length para verificar se um array está vazio ou não irão indicar que o array não está vazio.
    Alguns testes mostram que definir o tamanho inicial de arrays grandes pode ser mais eficiente se o array for preenchido posteriormente, mas o ganho de desempenho (se houver) parece variar de navegador para navegador.

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

    new Array(4);
    

    cria um array vazio de comprimento 4. Mas

    new Array('4');
    

    cria um array contendo o valor '4'.

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

var data = [];
var length = 5; // user defined length

for(var i = 0; i < length; i++) {
    data.push(createSomeObject());
}

Resposta #4

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

OU

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

Nota: você não pode percorrer posições vazias, ou seja, Array(4).forEach(() => …)


OU

( typescript seguro )

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

OU

Método clássico usando uma função (funciona em qualquer navegador)

function NewArray(size) {
    var x = [];
    for (var i = 0; i < size; ++i) {
        x[i] = i;
    }
    return x;
}

var a = NewArray(10);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Criando arrays aninhados

Ao criar um array 2D com o fill, intuitivamente deveria criar novas instâncias. Mas o que realmente vai acontecer é que o mesmo array será armazenado como uma 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, um array 3x2 vai parecer algo assim:

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

Array N-dimensional

function NArray(...dimensions) {
    var index = 0;
    function NArrayRec(dims) {
        var first = dims[0], next = dims.slice().splice(1); 
        if(dims.length > 1) 
            return Array(dims[0]).fill(null).map((x, i) => NArrayRec(next ));
        return Array(dims[0]).fill(null).map((x, i) => (index++));
    }
    return NArrayRec(dimensions);
}

var arr = NArray(3, 2, 4);
// [   [  [ 0,  1,  2,  3 ] , [  4,  5,  6,  7]  ],
//     [  [ 8,  9,  10, 11] , [ 12, 13, 14, 15]  ],
//     [  [ 16, 17, 18, 19] , [ 20, 21, 22, 23]  ]   ]

Inicializar um tabuleiro de xadrez

var Chessboard = [...Array(8)].map((x, j) => {
    return Array(8).fill(null).map((y, i) => {
        return `${String.fromCharCode(65 + i)}${8 - j}`;
    });
});

// [ [A8, B8, C8, D8, E8, F8, G8, H8],
//   [A7, B7, C7, D7, E7, F7, G7, H7],
//   [A6, B6, C6, D6, E6, F6, G6, H6],
//   [A5, B5, C5, D5, E5, F5, G5, H5],
//   [A4, B4, C4, D4, E4, F4, G4, H4],
//   [A3, B3, C3, D3, E3, F3, G3, H3],
//   [A2, B2, C2, D2, E2, F2, G2, H2],
//   [A1, B1, C1, D1, E1, F1, G1, H1] ]

Valores preenchidos por matemática

pequena sobrecarga de método prática ao trabalhar com matemática

function NewArray( size , method, linear )
{
    method = method || ( i => i ); 
    linear = linear || false;
    var x = [];
    for( var i = 0; i < size; ++i )
        x[ i ] = method( linear ? i / (size-1) : i );
    return x;
}

NewArray( 4 ); 
// [ 0, 1, 2, 3 ]

NewArray( 4, Math.sin ); 
// [ 0, 0.841, 0.909, 0.141 ]

NewArray( 4, Math.sin, true );
// [ 0, 0.327, 0.618, 0.841 ]

var pow2 = ( x ) => x * x;

NewArray( 4, pow2 ); 
// [ 0, 1, 4, 9 ]

NewArray( 4, pow2, true ); 
// [ 0, 0.111, 0.444, 1 ]

Resposta #5

O mais curto:

let arr = [...Array(10)];
console.log(arr);

Resposta #6

O ES6 introduz Array.from, que permite criar um Array a partir de quaisquer objetos "semelhantes a array" ou iteráveis:

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

Neste caso, {length: 10} representa a definição mínima de um objeto "semelhante a um array": um objeto vazio com apenas a propriedade length definida.

Array.from permite um segundo argumento para mapear sobre o array resultante.

Resposta #7

Arrays esparsos estão aqui! 🥳 [2021]

Em engines modernas de JS, arrays esparsos são totalmente suportados. Você pode usar [] ou new Array(len) da maneira que quiser, mesmo com acesso aleatório. O modo dicionário parece ser algo do passado.

No Chrome atual (e eu acho que em qualquer ambiente V8), Arrays podem ter um comprimento de até 2^32-1 e a alocação é esparsa (ou seja, partes vazias não usam memória):

No entanto, há um detalhe

Por um lado, os loops for funcionam como esperado, entretanto, as funções de ordem superior integradas do Array (como map, filter, find, some, etc.) ignoram elementos não atribuídos. Elas exigem fill (ou algum outro método de preenchimento) primeiro:

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

a.forEach(x => console.log(x)); // does nothing
b.forEach(x => console.log(x)); // works as intended

Versão Antiga

(Eu removi a maior parte da versão antiga.) A ideia principal era que criar um grande array usando new Array(largeNumber) ou acessar aleatoriamente um array em posições que ainda não foram alocadas faria com que ele entrasse no "modo dicionário". Isso significa que você está usando um array com índices, mas internamente ele usaria um dicionário para armazenar os valores, prejudicando assim o desempenho e também o comportamento da iteração. Felizmente, isso é coisa do passado.

Resposta #8

A forma mais simples é usar

Array.from({ length: 3 });

// gives you
[undefined, undefined, undefined]

Ao contrário de Array(3), que lhe dará um array sobre o qual você não pode iterar. Array.from({ length }) lhe dá um array sobre o qual você pode iterar facilmente.

Array.from({ length: 3 }).map((e, idx) => `hi ${idx}`);
// ['hi 1', 'hi 2', 'hi 3']

Resposta #9

Isso irá inicializar a propriedade length com 4:

var x = [,,,,];

Resposta #10

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

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

Pelos motivos mencionados acima, eu evitaria fazer isso a menos que quisesse inicializar o array com um valor específico. É interessante notar que existem outras bibliotecas que implementam range, incluindo Lo-dash e Lazy, que podem ter diferentes características de desempenho.

Resposta #11

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 ligação de objeto this, com a qual não nos importamos aqui, então definimos como null.

Array.apply(..) está chamando a função Array(..) e espalhando o valor do objeto { length: 3 } como seus argumentos.

Artigos relacionados