Come inizializzare la lunghezza di un array in JavaScript?


542

La maggior parte dei tutorial che ho letto su array in JavaScript (inclusi w3schools e devguru ) suggeriscono che è possibile inizializzare un array con una certa lunghezza passando un numero intero al costruttore dell'array usando la var test = new Array(4);sintassi.

Dopo aver usato questa sintassi liberamente nei miei file js, ho eseguito uno dei file attraverso jsLint e ha dato di matto :

Errore: problema alla riga 1 carattere 22: previsto ")" e invece visualizzato "4".
var test = new Array (4);
Problema alla riga 1 carattere 23: previsto ';' e invece ha visto ')'.
var test = new Array (4);
Problema alla riga 1 carattere 23: previsto un identificatore e invece visualizzato ")".

Dopo aver letto la spiegazione del comportamento di jsLint, sembra che jsLint non gradisca molto la new Array()sintassi e preferisce invece []dichiarare le matrici.

Quindi ho un paio di domande:

Innanzitutto, perché? Sto correndo qualche rischio usando new Array()invece la sintassi? Ci sono incompatibilità del browser di cui dovrei essere a conoscenza?

E in secondo luogo, se passo alla sintassi della parentesi quadra, c'è un modo per dichiarare un array e impostarne la lunghezza su una riga o devo fare qualcosa del genere:

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

js standard, consigliano anche contro usandonew Array()in generale, ma va bene con specifica dimensione. Penso che tutto si riduce alla coerenza del codice in tutto il contesto.
Cregox,

Per coloro che desiderano preallocare strutture di array più rigide, ci sono array tipizzati . Si noti che i vantaggi in termini di prestazioni possono variare
rovyko,

Risposte:


397
  1. Perché vuoi inizializzare la lunghezza? Teoricamente non ce n'è bisogno. Può anche comportare un comportamento confuso, poiché tutti i test che usano il lengthper scoprire se un array è vuoto o no segnalerà che l'array non è vuoto.
    Alcuni test mostrano che l'impostazione della lunghezza iniziale di array di grandi dimensioni può essere più efficiente se l'array viene successivamente riempito, ma il guadagno delle prestazioni (se presente) sembra differire da browser a browser.

  2. jsLint non piace new Array()perché il costruttore è ambiguo.

    new Array(4);

    crea una matrice vuota di lunghezza 4. Ma

    new Array('4');

    crea una matrice contenente il valore '4' .

Per quanto riguarda il tuo commento: in JS non è necessario inizializzare la lunghezza dell'array. Cresce dinamicamente. Puoi semplicemente memorizzare la lunghezza in alcune variabili, ad es

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

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

13
Il numero di oggetti nell'array è definito dall'utente, quindi stavo lasciando che l'utente selezionasse un numero, quindi inizializzavo l'array con così tanti slot. Quindi eseguo un forciclo che scorre lungo la lunghezza dell'array e lo riempie. Immagino che ci sarebbero altri modi per farlo in JavaScript, quindi la vera risposta a "Perché voglio farlo?" è "A causa delle vecchie abitudini che si sono formate durante la programmazione in altre lingue". :)
Michael Martin-Smucker,

4
@mlms Basta lasciare che il ciclo for esegua l'iterazione sul numero definito dall'utente invece di dover impostare la lunghezza dell'array e quindi iterare su di esso. Non ha più senso per te?
Šime Vidas,

151
<blockquote> Perché vuoi inizializzare la lunghezza? Teoricamente non ce n'è bisogno. E tutti i test che usano la lunghezza per scoprire se un array è vuoto o no falliranno. </blockquote> Uhm, forse le prestazioni ? È più veloce impostare un elemento preesistente di un array piuttosto che aggiungerlo al volo.
codehead

45
Il commento "tempo di rinunciare alle vecchie abitudini di programmazione" è davvero superfluo. linguaggi correttamente strutturati supereranno sempre ecmascript mombo jambo
user151496

10
@codehead: in risposta a questa citazione: "È più veloce impostare un elemento preesistente di un array piuttosto che aggiungerlo al volo.": nota che new Array(10)non crea un array con 10 elementi indefiniti. Esso crea semplicemente un array vuoto con una lunghezza di 10. Vedere questa risposta per la brutta verità: stackoverflow.com/questions/18947892/...
millimetrica

598
  • Array(5) ti dà un array con lunghezza 5 ma nessun valore, quindi non puoi iterare su di esso.

  • Array.apply(null, Array(5)).map(function () {}) ti dà un array con lunghezza 5 e non definito come valori, ora può essere ripetuto.

  • Array.apply(null, Array(5)).map(function (x, i) { return i; }) ti dà un array con lunghezza 5 e valori 0,1,2,3,4.

  • Array(5).forEach(alert)non fa nulla, Array.apply(null, Array(5)).forEach(alert)ti dà 5 avvisi

  • ES6ci dà Array.fromcosì ora puoi anche usareArray.from(Array(5)).forEach(alert)

  • Se vuoi inizializzare con un certo valore, questi sono buoni a sapersi ...
    Array.from('abcde'), Array.from('x'.repeat(5))
    oArray.from({length: 5}, (v, i) => i) // gives [0, 1, 2, 3, 4]


11
Questo è quello che stavo cercando. Volevo applicare una mappa su una sequenza logica; questo dovrebbe farlo. Grazie!
jedd.ahyoung,

3
perché Array.apply () non lo definisce come valore?
wdanxna,

5
@wdanxna quando Arrayvengono forniti più argomenti, scorre argumentssull'oggetto e applica esplicitamente ogni valore al nuovo array. Quando si chiama Array.applycon un array o un oggetto con una proprietà length Arrayverrà utilizzata la lunghezza per impostare in modo esplicito ciascun valore del nuovo array. Questo è il motivo per cui Array(5)dà un array di 5 elisioni, mentre Array.apply(null, Array(5))fornisce un array di 5 indefiniti. Per ulteriori informazioni, vedere questa risposta.
KylePlusPlus

2
c'è differenza tra array (5) e nuovo array (5)?
Petr Hurtak,

2
Il Array.apply(null, Array(5)) può anche essere scritto come Array.apply(null, {length: 5}). Non c'è davvero molta differenza, ma quest'ultimo è inequivocabilmente chiaro che l'intento è quello di creare un array di length 5, e non un array contenente5
AlexMorley-Finch

272

Con ES2015.fill() ora puoi semplicemente fare:

// `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)

Che è molto più conciso di Array.apply(0, new Array(n)).map(i => value)

È possibile rilasciare 0in .fill()ed eseguire senza argomenti, che riempirà l'array con undefined. ( Tuttavia, questo non funzionerà in Typescript )


1
@AralRoca È sempre possibile utilizzare Polyfill fornito nella pagina MDN o prendere in considerazione l'utilizzo di Modernizr .
AP.

4
Sì, per favore provalo prima di commentare
AP.

1
@GarretWilson e @Christian È nelle specifiche . When Array is called as a function rather than as a constructor, it also creates and initializes a new Array object. Thus the function call Array(…) is equivalent to the object creation expression new Array(…) with the same arguments
AP.

1
@AP., Lo zero è ridondante. DoArray(n).fill()
Pacerier,

2
@Pacerier Non credo che lo 0 fosse ridondante. Secondo la definizione del tipo es6, questa è la firma della funzione: fill (valore: T, inizio ?: numero, fine ?: numero): questo; Quindi il valore di riempimento non sembra facoltativo. La compilazione dei dattiloscritti fallirà quando la chiamata alla funzione è scritta sopra.
Micha Schwab,

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

O

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

Nota: non è possibile eseguire il loop di slot vuoti, ad es Array(4).forEach(() => …)


O

( dattiloscritto sicuro )

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

O

Metodo classico che utilizza una funzione (funziona in qualsiasi browser)

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]

Creazione di array nidificati

Quando si crea un array 2D con l' fillintuitivamente dovrebbe creare nuove istanze. Ma ciò che realmente accadrà è che lo stesso array verrà archiviato come riferimento.

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

a[0].push(9);
// [  [6, 9], [6, 9], [6, 9]  ]

Soluzione

var a = [...Array(3)].map(x => []);

a[0].push(4, 2);
// [  [4, 2], [], []  ]

Quindi un array 3x2 avrà un aspetto simile al seguente:

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

Matrice N-dimensionale

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

Inizializza una scacchiera

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

1
a[0].push(9); // [ [6, 9], [6], [6] ] dovrebbe essere a[0].push(9); // [ [6, 9], [6, 9], [6, 9] ] perché ogni array nidificato viene archiviato come riferimento, quindi la modifica di un array influisce sul resto. Potresti averlo
scritto male

@Alex_Zhong vero che, grazie, l'ho perso in una modifica
Vlad

68

Il più corto:

[...Array(1000)]

11
Eseguito l'upgrade, ma un avvertimento, non dovresti assolutamente iniziare una linea in JS con a [senza usare a ;poiché tenterà di dereferenziare la linea sopra. Quindi un modo sicuro per usare questa linea in modo prevedibile in qualsiasi parte del codice sarebbe:;[...Array(1000)]//.whateverOpsNeeded()
AP.

non proprio. prova con gli strumenti per sviluppatori. [...Array(5)].map((item, index) => ({ index })) prova anche questo:[...Array(5)]; console.log('hello');
Michael Mammoliti,

2
Prova a usarlo in un file js multilinea. Se la tua prima riga è const a = 'a'e la riga successiva [...Array(5)].//irrelevent. Cosa pensi che la prima riga risolverebbe? Sarebbe const a = 'a'[...Array(5)]che si tradurrebbe in:Uncaught SyntaxError: Unexpected token ...
AP.

7
È vero, ma in questo caso direi che il problema è da qualche altra parte, punti e virgola sono importanti al giorno d'oggi.
Michael Mammoliti,

8
@AP quell'esempio è esattamente il motivo per cui molte guide di stile richiedono di terminare ogni istruzione con un punto e virgola. const a = 'a'sarà un errore Lint in quel caso. Comunque, non ha davvero nulla a che fare con questa risposta.
attacco

41

ES6 introduce Array.fromche consente di creare un Arrayqualsiasi oggetto "simile a array" o iterable :

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

In questo caso {length: 10}rappresenta la definizione minima di un oggetto "simile a matrice" : un oggetto vuoto con solo una lengthproprietà definita.

Array.from consente un secondo argomento da mappare sull'array risultante.


2
@dain Penso che [... Array (10)] è meglio stackoverflow.com/a/47929322/4137472
Alexander Shutau

1
Perché meglio? L'uso del costruttore di array è sconsigliato e alcuni linter potrebbero anche lamentarsi
Cristian Traìna,

26

Questo inizializzerà la proprietà length su 4:

var x = [,,,,];

3
È intelligente. Non ha la flessibilità del costruttore, perché non è possibile utilizzare una variabile per impostare la lunghezza, ma risponde alla mia domanda come la avevo originariamente formulata, quindi +1.
Michael Martin-Smucker,

12
Immagina di farlo per 300 articoli quando le prestazioni contano davvero!
Marco Luglio,

9
Probabilmente non c'è molto guadagno in termini di prestazioni quando si predispone un array con dimensioni così ridotte. La differenza di prestazioni verrà percepita meglio quando si creano array più grandi. E in quei casi, inizializzarli con virgole sarebbe un po 'travolgente.
Marco Luglio,

6
Penso che questo produrrà risultati diversi nei vari browser a causa della virgola finale, a volte 4 e 5 volte, vedere stackoverflow.com/questions/7246618/...
jperelli

1
Che ne dici var size = 42; var myArray = eval("["+",".repeat(size)+"]");? (Non così grave;)
xoxox

15

Sono sorpreso che non sia stata suggerita una soluzione funzionale che ti consenta di impostare la lunghezza su una riga. Quanto segue si basa su UnderscoreJS:

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

Per i motivi sopra menzionati, eviterei di farlo a meno che non volessi inizializzare l'array su un valore specifico. È interessante notare che ci sono altre librerie che implementano la gamma tra cui Lo-dash e Lazy, che possono avere caratteristiche prestazionali diverse.


La sottolineatura della magia nera non è davvero necessaria qui. Le dipendenze sono come lo zucchero, all'inizio sembra dolce, ma prima che tu lo sappia hai il diabete.
Romain Vincent,

9

Per favore, le persone non rinunciano ancora alle tue vecchie abitudini. Esiste una grande differenza nella velocità tra l'allocazione della memoria una volta che poi si lavora con le voci in quell'array (come una volta) e l'allocazione molte volte man mano che un array cresce (che è inevitabilmente ciò che il sistema fa sotto il cofano con altri metodi suggeriti) .

Niente di tutto questo ovviamente, finché non vuoi fare qualcosa di interessante con array più grandi. Quindi lo fa.

Dato che al momento sembra non esserci ancora alcuna opzione in JS per impostare la capacità iniziale di un array, utilizzo il seguente ...

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

Ci sono compromessi coinvolti:

  • Questo metodo richiede tanto quanto gli altri per la prima chiamata alla funzione, ma pochissimo tempo per le chiamate successive (a meno che non venga richiesto un array più grande).
  • L' standardarray riserva permanentemente tanto spazio quanto l'array più grande richiesto.

Ma se si adatta a quello che stai facendo, può esserci un profitto. La messa a punto informale mette

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

abbastanza veloce (circa 50ms per i 10000 dato che con n = 1000000 ci sono voluti circa 5 secondi), e

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

a ben più di un minuto (circa 90 secondi per il 10000 sulla stessa console Chrome, o circa 2000 volte più lento). Non sarà solo l'allocazione, ma anche i 10000 push, per loop, ecc.


se ogni volta che raggiungi la fine dell'array lo duplichi, la complessità dell'inserimento di n valori è ancora O (n), quindi non c'è differenza nella complessità (ma è più lenta).
Tomer Wolberg,

Ben preso @TomerWolberg, ho cambiato la mia formulazione. Si tratta di stabilire se il metodo push ha una complessità maggiore rispetto all'inizializzazione / copia di un singolo membro per sezione. AFAIK che sono entrambi tempi costanti, per lo meno potrebbero essere, quindi ho usato la parola "velocità".
termina il

8

(probabilmente era meglio come commento, ma è diventato troppo lungo)

Quindi, dopo aver letto questo, ero curioso di sapere se la pre-allocazione fosse effettivamente più veloce, perché in teoria dovrebbe esserlo. Tuttavia, questo blog ha fornito alcuni consigli che lo sconsigliano http://www.html5rocks.com/en/tutorials/speed/v8/ .

Quindi, non essendo sicuro, l'ho messo alla prova. E a quanto pare sembra in effetti essere più 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); 

Questo codice produce quanto segue dopo alcune esecuzioni casuali:

$ 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

3
Interessante, mi è sembrato inaspettato, quindi ho fatto un test JSPerf . I risultati di Chrome corrispondono ai test del nodo, ma Firefox e IE sono leggermente più veloci quando si alloca spazio per l'array.
Michael Martin-Smucker,

1
@ MichaelMartin-Smucker Aspetta ... significa che V8 non è in realtà completamente ottimizzato e perfetto?!?!? : P
Zeb McCorkle,

1
@ MichaelMartin-Smucker - Ho appena eseguito il tuo jsperf in Chrome versione 42.0.2311.90 (per essere precisi - Test in Chrome 42.0.2311.90 a 32 bit su Windows Server 2008 R2 / 7 a 64 bit) e un array di dimensioni dinamiche era più lento del 69% .
Yellen,

1
Idem. (Ho scritto il test del nodo originale) In Chrome 42.0.2311.90 su Windows la dimensione dinamica era dell'81% più lenta :). Ma i nostri test originali erano finiti un anno fa. Il tempo continua a scivolare, scivolare ...
j03m

1
Affascinante ... dai risultati su jsperf sembra che la pre-allocazione abbia avuto un enorme impulso in Chrome 38. Il futuro!
Michael Martin-Smucker,

8
var arr=[];
arr[5]=0;
alert("length="+arr.length); // gives 6

2
Sì, comunque console.log (arr) fornisce [5: 0]una matrice sparsa e probabilmente non è ciò che si desidera.
Dreftymac,

7

Ecco un'altra soluzione

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

Il primo argomento di questo apply()è un vincolo di questo oggetto, che non ci interessa qui, quindi lo impostiamo su null.

Array.apply(..)chiama la Array(..)funzione e distribuisce il { length: 3 }valore dell'oggetto come suoi argomenti.


Non capisco perché questa risposta abbia ottenuto un voto negativo. In realtà è un bel trucco. new Array(n)Ciò significa che non è possibile utilizzare per inizializzare un array come questo new Array(n).map(function(notUsed,index){...}), ma con questo approccio, come menzionato da @zangw, è possibile farlo. +1 da me.
Victor.Palyvoda,

3

Il costruttore dell'array ha una sintassi ambigua e JSLint fa male ai tuoi sentimenti dopo tutto.

Inoltre, il tuo codice di esempio è rotto, la seconda varistruzione genererà a SyntaxError. Stai impostando la proprietà lengthdell'array test, quindi non è necessario un altrovar .

Per quanto riguarda le tue opzioni, array.lengthè l'unica "pulita". La domanda è: perché devi prima impostare le dimensioni? Prova a riformattare il codice per sbarazzarti di quella dipendenza.


Woops, buon occhio su quel secondo var test. Quella è stata una copia e incolla trascurata da parte mia.
Michael Martin-Smucker,

@IvoWetzel, vuoi impostare le dimensioni per migliorare le prestazioni. Le matrici JS sono matrici dinamiche . Se non si imposta la dimensione (o meglio, è la capacità), JS assegnerà un array di dimensioni predefinite. Se poi aggiungi più elementi di quanti ne possano contenere, dovrà far crescere l'array, il che significa che internamente allocerà un nuovo array e quindi copierà tutti gli elementi.
Domi,

3

Supponendo che la lunghezza dell'array sia costante. In Javascript, questo è ciò che facciamo:

const intialArray = new Array(specify the value);


2

Come spiegato sopra, l'uso new Array(size)è alquanto pericoloso. Invece di usarlo direttamente, inseriscilo in una "funzione creatore di array". Puoi facilmente assicurarti che questa funzione sia priva di bug ed evitare il pericolo di chiamare new Array(size)direttamente. Inoltre, puoi assegnargli un valore iniziale predefinito opzionale. Questa createArrayfunzione fa esattamente questo:

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

1

Nella maggior parte delle risposte, si consiglia fillall'array perché altrimenti " non si può iterare su di esso" , ma questo non è vero. Puoi iterare un array vuoto, ma non con forEach. Mentre i loop, per i loop e per i loop funzionano bene.

const count = Array(5);

Non funziona.

console.log('---for each loop:---');
count.forEach((empty, index) => {
    console.log(`counting ${index}`);
});

Questi lavori:

console.log('---for of loop:---');
for (let [index, empty] of count.entries()) {
  console.log(`counting for of loop ${index}`);
}

console.log('---for i loop:---');
for (let i = 0, il = count.length; i < il; ++i) {
  console.log(`counting for i loop ${i}`);
}

console.log('---while loop:---');
let index = 0;
while (index < count.length) { 
  console.log(`counting while loop ${index}`); 
  index++; 
}

Controlla questo violino con gli esempi sopra.

Anche gli angolari *ngForfunzionano bene con un array vuoto:

<li *ngFor="let empty of count; let i = index" [ngClass]="
  <span>Counting with *ngFor {{i}}</span>
</li>

-1

È possibile impostare la lunghezza dell'array utilizzando array.length = youValue

Così sarebbe

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

-3

Il motivo per cui non dovresti usare new Arrayè dimostrato da questo codice:

var Array = function () {};

var x = new Array(4);

alert(x.length);  // undefined...

Qualche altro codice potrebbe interferire con la variabile Array. So che è un po 'inverosimile che qualcuno scriva questo codice, ma comunque ...

Inoltre, come ha detto Felix King, l'interfaccia è un po 'incoerente e potrebbe portare ad alcuni bug molto difficili da rintracciare.

Se si desidera un array con lunghezza = x, riempito con indefinito (come new Array(x)si farebbe), è possibile farlo:

var x = 4;
var myArray = [];
myArray[x - 1] = undefined;

alert(myArray.length); // 4

48
Secondo questo ragionamento non dovresti usare alerte undefined, poiché alcuni altri codici potrebbero confonderli. Il secondo esempio è meno leggibile di new Array(4)e non ti dà lo stesso risultato: jsfiddle.net/73fKd
Alexey Lebedev

25
Questa risposta è bizzarra
Marco Demaio,

6
Non puoi evitare che le persone facciano casino con oggetti globali in JavaScript; semplicemente non farlo o utilizzare framework in cui le persone lo fanno.
Richard Connamacher,

In Chrome sostitu attualmente: j = new Array (4); j.length; // risulta in 4
Parris,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.