¿Cómo inicializar la longitud de una matriz en JavaScript?

542

La mayoría de los tutoriales que he leído en matrices en JavaScript (incluidos w3schools y devguru ) sugieren que puede inicializar una matriz con una cierta longitud pasando un número entero al constructor de matrices utilizando la var test = new Array(4);sintaxis.

Después de usar esta sintaxis generosamente en mis archivos js, ejecuté uno de los archivos a través de jsLint , y se asustó:

Error: Problema en la línea 1, carácter 22: Se esperaba ')' y en su lugar vio '4'.
prueba var = nueva matriz (4);
Problema en la línea 1, carácter 23: Se esperaba ';' y en su lugar vi ')'.
prueba var = nueva matriz (4);
Problema en la línea 1, carácter 23: esperaba un identificador y en su lugar vio ')'.

Después de leer la explicación de jsLint de su comportamiento , parece que a jsLint realmente no le gusta la new Array()sintaxis y, en cambio, prefiere []al declarar matrices.

Entonces tengo un par de preguntas:

Primero, ¿por qué? ¿Corro algún riesgo al utilizar la new Array()sintaxis? ¿Hay incompatibilidades del navegador que debo tener en cuenta?

Y segundo, si cambio a la sintaxis de corchetes, ¿hay alguna forma de declarar una matriz y establecer su longitud en una sola línea, o tengo que hacer algo como esto:

var test = [];
test.length = 4;
Michael Martin-Smucker
fuente
js estándar también aconsejan contra el usonew Array()en general, pero está bien con la especificación de tamaño. Creo que todo se reduce a la consistencia del código en todo el contexto.
cregox 01 de
Para aquellos que buscan preasignar estructuras de matriz más rígidas, hay matrices con tipo . Tenga en cuenta que los beneficios de rendimiento pueden variar
rovyko

Respuestas:

397
  1. ¿Por qué quieres inicializar la longitud? Teóricamente no hay necesidad de esto. Incluso puede dar lugar a un comportamiento confuso, porque todas las pruebas que usan el lengthpara averiguar si una matriz está vacía o no informarán que la matriz no está vacía.
    Algunas pruebas muestran que establecer la longitud inicial de las matrices grandes puede ser más eficiente si la matriz se llena después, pero la ganancia de rendimiento (si la hay) parece diferir de un navegador a otro.

  2. A jsLint no le gusta new Array()porque el constructor es ambiguo.

    new Array(4);

    crea una matriz vacía de longitud 4. Pero

    new Array('4');

    crea una matriz que contiene el valor '4' .

Con respecto a su comentario: en JS no necesita inicializar la longitud de la matriz. Crece dinámicamente. Puede almacenar la longitud en alguna variable, p. Ej.

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

for(var i = 0; i < length; i++) {
    data.push(createSomeObject());
}
Felix Kling
fuente
13
El número de objetos en la matriz está definido por el usuario, por lo que estaba dejando que el usuario eligiera un número y luego inicializando la matriz con esa cantidad de ranuras. Luego ejecuto un forbucle que itera a lo largo de la matriz y lo llena. Supongo que habría otras formas de hacer esto en JavaScript, por lo que la respuesta real a "¿Por qué quiero hacer esto?" es "Debido a los viejos hábitos que se formaron durante la programación en otros lenguajes". :)
Michael Martin-Smucker
44
@mlms Simplemente deje que el bucle for itere sobre el número definido por el usuario en lugar de tener que establecer la longitud de la matriz y luego iterar sobre él. ¿Eso no tiene más sentido para ti?
Šime Vidas
151
<blockquote> ¿Por qué quieres inicializar la longitud? Teóricamente no hay necesidad de esto. Y todas las pruebas que usan la longitud para averiguar si una matriz está vacía o no fallarán. </blockquote> Um, ¿quizás rendimiento ? Es más rápido establecer un elemento preexistente de una matriz que agregarlo sobre la marcha.
codehead
45
El comentario sobre "tiempo para abandonar viejos hábitos de programación" es realmente innecesario. los lenguajes correctamente estructurados siempre superarán a ecmascript mombo jambo
user151496
10
@codehead: en respuesta a esta cita: "Es más rápido establecer un elemento preexistente de una matriz que agregarlo sobre la marcha": tenga en cuenta que new Array(10)no crea una matriz con 10 elementos indefinidos. Simplemente crea una matriz vacía con una longitud de 10. Vea esta respuesta para la desagradable verdad: stackoverflow.com/questions/18947892/…
Milimetric
599
  • Array(5) le da una matriz con longitud 5 pero sin valores, por lo tanto, no puede iterar sobre ella.

  • Array.apply(null, Array(5)).map(function () {}) le ofrece una matriz con longitud 5 e indefinida como valores, ahora se puede repetir.

  • Array.apply(null, Array(5)).map(function (x, i) { return i; }) le da una matriz con longitud 5 y valores 0,1,2,3,4.

  • Array(5).forEach(alert)no hace nada, Array.apply(null, Array(5)).forEach(alert)te da 5 alertas

  • ES6nos da Array.fromasí que ahora también puedes usarArray.from(Array(5)).forEach(alert)

  • Si desea inicializar con un cierto valor, es bueno saberlo ...
    Array.from('abcde'), Array.from('x'.repeat(5))
    oArray.from({length: 5}, (v, i) => i) // gives [0, 1, 2, 3, 4]

Ruben Stolk
fuente
11
Esto es lo que estaba buscando. Quería aplicar un mapa sobre una secuencia lógica; Esto debería hacerlo. ¡Gracias!
jedd.ahyoung
3
¿Por qué Array.apply () le da un valor indefinido?
wdanxna 01 de
55
@wdanxna cuando Arrayrecibe múltiples argumentos, itera sobre el argumentsobjeto y aplica explícitamente cada valor a la nueva matriz. Cuando llame Array.applycon una matriz o un objeto con una propiedad de longitud, Arrayusará la longitud para establecer explícitamente cada valor de la nueva matriz. Es por eso que Array(5)da una matriz de 5 elisiones, mientras que Array.apply(null, Array(5))da una matriz de 5 indefinidos. Para más información, vea esta respuesta.
KylePlusPlus
2
¿Hay alguna diferencia entre la matriz (5) y la nueva matriz (5)?
Petr Hurtak
2
El Array.apply(null, Array(5)) también se puede escribir como Array.apply(null, {length: 5}). Realmente no hay mucha diferencia, pero este último es inequívocamente claro que la intención es crear una matriz de length 5, y no una matriz que contenga5
AlexMorley-Finch
272

Con ES2015.fill() ahora puede simplemente hacer:

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

Lo cual es mucho más conciso que Array.apply(0, new Array(n)).map(i => value)

Es posible dejar caer el 0en .fill()y ejecutar sin argumentos, que va a llenar la matriz con undefined. ( Sin embargo, esto fallará en Typecript )

AP.
fuente
1
@AralRoca Siempre puede usar el Polyfill provisto en la página MDN, o considerar usar Modernizr .
AP.
44
Sí, pruébalo antes de comentar
AP.
1
@GarretWilson y @Christian está en la especificación . 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., El cero es redundante. DoArray(n).fill()
Pacerier
2
@Pacerier No creo que el 0 fuera redundante. Según la definición de tipo es6, esta es la firma de la función: fill (valor: T, start ?: number, end ?: number): this; Por lo tanto, el valor de relleno no parece opcional. La compilación mecanografiada fallará ya que la llamada a la función se escribe arriba.
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: no puede hacer bucles de espacios vacíos, es decir Array(4).forEach(() => …)


O

( mecanografiado seguro )

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

O

Método clásico que utiliza una función (funciona en cualquier 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]

Crear matrices anidadas

Al crear una matriz 2D con la fillintuitiva debe crear nuevas instancias. Pero lo que realmente sucederá es que la misma matriz se almacenará como referencia.

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

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

Solución

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

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

Entonces, una matriz 3x2 se verá así:

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

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

Inicializa un tablero de ajedrez

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] ]
Vlad
fuente
1
a[0].push(9); // [ [6, 9], [6], [6] ] debería deberse a a[0].push(9); // [ [6, 9], [6, 9], [6, 9] ] que cada matriz anidada se almacena como referencia, por lo que mutar una matriz afecta al resto. Puede que lo hayas escrito mal aunque creo :)
Alex_Zhong
@Alex_Zhong tru que, gracias, perdió eso en una edición
Vlad
68

El más corto:

[...Array(1000)]
Michael Mammoliti
fuente
11
Votado a favor, pero una advertencia, absolutamente no debes comenzar una línea en JS con a [sin usar un ;ya que intentará desreferenciar la línea de arriba. Entonces, una forma segura de usar esta línea de manera predecible en cualquier parte del código sería:;[...Array(1000)]//.whateverOpsNeeded()
AP.
realmente no. probar en herramientas de desarrollador. [...Array(5)].map((item, index) => ({ index })) intente esto también:[...Array(5)]; console.log('hello');
Michael Mammoliti
2
Intente usarlo en un archivo js de varias líneas. Si su primera línea es const a = 'a'y la siguiente línea [...Array(5)].//irrelevent. ¿A qué crees que se resolvería la primera línea? Sería const a = 'a'[...Array(5)]lo que resultaría en:Uncaught SyntaxError: Unexpected token ...
AP.
77
Eso es cierto, pero en este caso diría que el problema está en otro lugar, los puntos y comas son importantes hoy en día.
Michael Mammoliti
8
@AP ese ejemplo es exactamente la razón por la cual muchas guías de estilo obligan a terminar cada declaración con un punto y coma. const a = 'a'será un error de pelusa en ese caso. De todos modos, realmente no tiene nada que ver con esta respuesta.
Graup
41

ES6 presenta lo Array.fromque le permite crear un objeto Arraydesde cualquier objeto "tipo matriz" o iterable :

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

En este caso {length: 10}representa la definición mínima de un objeto "tipo matriz" : un objeto vacío con solo una lengthpropiedad definida.

Array.from permite un segundo argumento para mapear sobre la matriz resultante.

Alexander Shutau
fuente
2
@dain Creo que [... Array (10)] es mejor stackoverflow.com/a/47929322/4137472
Alexander Shutau
1
¿Por qué mejor? Se desaconseja el uso del constructor de matrices, y algunos linter también podrían quejarse
Cristian Traìna
26

Esto inicializará la propiedad de longitud a 4:

var x = [,,,,];
Šime Vidas
fuente
3
Eso es inteligente. No tiene la flexibilidad del constructor, porque no puede usar una variable para establecer la longitud, pero responde a mi pregunta tal como la expresé originalmente, por lo que +1.
Michael Martin-Smucker
12
¡Imagínese hacer eso por 300 artículos cuando el rendimiento realmente importaría!
Marco Luglio
99
Probablemente no haya mucha ganancia de rendimiento al preasignar una matriz con un tamaño tan pequeño. La diferencia de rendimiento se percibirá mejor al crear matrices más grandes. Y en esos casos, inicializarlos con comas sería un poco abrumador.
Marco Luglio
66
Creo que esto producirá resultados diferentes en diferentes navegadores debido a la coma final, a veces 4 y otras 5, consulte stackoverflow.com/questions/7246618/…
jperelli el
1
¿Qué tal var size = 42; var myArray = eval("["+",".repeat(size)+"]");? (No es tan grave;)
xoxox
15

Me sorprende que no se haya sugerido una solución funcional que le permita establecer la longitud en una línea. Lo siguiente se basa en UnderscoreJS:

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

Por las razones mencionadas anteriormente, evitaría hacer esto a menos que quisiera inicializar la matriz a un valor específico. Es interesante observar que hay otras bibliotecas que implementan el rango, incluidos Lo-dash y Lazy, que pueden tener diferentes características de rendimiento.

christang
fuente
La magia negra de subrayado realmente no es necesaria aquí. Las dependencias son como el azúcar, parece dulce al principio, pero antes de que te des cuenta tienes diabetes.
Romain Vincent
9

Por favor, la gente todavía no abandona tus viejos hábitos. Hay una gran diferencia en la velocidad entre asignar memoria una vez y luego trabajar con las entradas en esa matriz (como antes), y asignarla muchas veces a medida que crece una matriz (que es inevitablemente lo que el sistema hace bajo el capó con otros métodos sugeridos) .

Nada de esto importa, por supuesto, hasta que desee hacer algo genial con matrices más grandes. Entonces lo hace.

Al ver que todavía no parece haber ninguna opción en JS en este momento para establecer la capacidad inicial de una matriz, utilizo lo siguiente ...

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

Hay compensaciones involucradas:

  • Este método tarda tanto como los demás para la primera llamada a la función, pero muy poco tiempo para las llamadas posteriores (a menos que solicite una matriz más grande).
  • La standardmatriz reserva permanentemente tanto espacio como la matriz más grande que haya solicitado.

Pero si encaja con lo que estás haciendo, puede haber una recompensa. Puestos informales de tiempo

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

a bastante velocidad (unos 50 ms para el 10000 dado que con n = 1000000 tardó unos 5 segundos), y

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

más de un minuto (aproximadamente 90 segundos para el 10000 en la misma consola de Chrome, o aproximadamente 2000 veces más lento). Esa no solo será la asignación, sino también los 10000 empujes, bucles, etc.

wils
fuente
si cada vez que llega al final de la matriz la duplica, la complejidad de insertar n valores sigue siendo O (n), por lo que no hay diferencia en la complejidad (pero es más lenta).
Tomer Wolberg
Bien atrapado @TomerWolberg, he cambiado mi redacción. Se trata de si el método push tiene una mayor complejidad que la inicialización / copia de un miembro individual por segmento. AFAIK de que ambos son de tiempo constante, por lo menos podrían ser así que he usado la palabra 'velocidad' en su lugar.
Wils
8

(esto probablemente fue mejor como comentario, pero se hizo demasiado largo)

Entonces, después de leer esto, tenía curiosidad por saber si la preasignación era realmente más rápida, porque en teoría debería serlo. Sin embargo, este blog dio algunos consejos que lo desaconsejan http://www.html5rocks.com/en/tutorials/speed/v8/ .

Así que todavía no estoy seguro, lo puse a prueba. Y resulta que, de hecho, parece ser más 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 produce lo siguiente después de algunas ejecuciones ocasionales:

$ 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
j03m
fuente
3
Interesante, eso parecía inesperado, así que hice una prueba JSPerf . Los resultados de Chrome coinciden con sus pruebas de Nodo, pero Firefox e IE son un poco más rápidos cuando preasigna espacio para la matriz.
Michael Martin-Smucker
1
@ MichaelMartin-Smucker Espera ... ¿esto significa que V8 no está realmente completamente optimizado y perfecto?!?!? : P
Zeb McCorkle
1
@ MichaelMartin-Smucker: acabo de ejecutar su jsperf en la versión 42.0.2311.90 de Chrome (para ser específicos: pruebas en Chrome 42.0.2311.90 de 32 bits en Windows Server 2008 R2 / 7 de 64 bits) y la matriz de tamaño dinámico fue un 69% más lenta .
Yellen
1
Ídem. (Escribí la prueba de nodo original) En Chrome 42.0.2311.90 en Windows, el tamaño dinámico fue un 81% más lento :). Pero nuestras pruebas originales fueron hace más de un año. El tiempo sigue resbalando, resbalando ...
j03m
1
Fascinante ... a partir de los resultados en jsperf parece que la asignación previa recibió un gran impulso en Chrome 38. ¡El futuro!
Michael Martin-Smucker
8
var arr=[];
arr[5]=0;
alert("length="+arr.length); // gives 6
usuario1067305
fuente
2
Sí, sin embargo, console.log (arr) da que [5: 0]esta es una matriz dispersa y probablemente no sea lo que se desea.
dreftymac
7

Aquí hay otra solución

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

El primer argumento de apply()es un enlace de este objeto, que no nos importa aquí, así que lo configuramos en null.

Array.apply(..)llama a la Array(..)función y extiende el { length: 3 }valor del objeto como sus argumentos.

zangw
fuente
No entiendo por qué esta respuesta recibió un voto negativo. En realidad es un buen truco. Lo que significa que no puede usar new Array(n)para inicializar una matriz como esta new Array(n).map(function(notUsed,index){...}), pero con este enfoque, como mencionó @zangw, puede hacerlo. +1 de mi parte
Victor.Palyvoda
3

El constructor de matrices tiene una sintaxis ambigua , y JSLint solo hiere tus sentimientos después de todo.

Además, su código de ejemplo está roto, la segunda varinstrucción generará un SyntaxError. Estás configurando la propiedad lengthde la matriz test, por lo que no hay necesidad de otra var.

En cuanto a sus opciones, array.lengthes la única "limpia". La pregunta es, ¿por qué necesita establecer el tamaño en primer lugar? Intenta refactorizar tu código para deshacerte de esa dependencia.

Ivo Wetzel
fuente
Woops, buen ojo en ese segundo var test. Esa fue una copia descuidada y pegado de mi parte.
Michael Martin-Smucker
@IvoWetzel, desea establecer el tamaño para mejorar el rendimiento. Las matrices JS son matrices dinámicas . Si no establece el tamaño (o más bien, su capacidad), JS asignará una matriz de tamaño predeterminado. Si luego agrega más elementos de los que caben, tendrá que hacer crecer la matriz, lo que significa que internamente asignará una nueva matriz y luego copiará todos los elementos.
Domi
3

Suponiendo que la longitud de la matriz es constante. En Javascript, esto es lo que hacemos:

const intialArray = new Array(specify the value);

mohan babu
fuente
2

Como se explicó anteriormente, el uso new Array(size)es algo peligroso. En lugar de usarlo directamente, colóquelo dentro de una "función creadora de matriz". Puede asegurarse fácilmente de que esta función esté libre de errores y evitar el peligro de llamar new Array(size)directamente. Además, puede darle un valor inicial predeterminado opcional. Esta createArrayfunción hace exactamente eso:

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;
}
Domi
fuente
1

En la mayoría de las respuestas se recomienda a fillla matriz porque de lo contrario "no se puede iterar sobre ella" , pero esto no es cierto. Puede iterar una matriz vacía, pero no con forEach. Mientras que los bucles, for of y for i loops funcionan bien.

const count = Array(5);

No funciona.

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

Estos trabajan:

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

Mira este violín con los ejemplos anteriores.

También los angulares *ngForfuncionan bien con una matriz vacía:

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

Puede establecer la longitud de la matriz usando array.length = youValue

Entonces sería

var myArray = [];
myArray.length = yourValue;
ajoposor
fuente
-3

new ArrayEste código demuestra la razón por la que no debe usar :

var Array = function () {};

var x = new Array(4);

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

Algún otro código podría interferir con la variable Array. Sé que es un poco descabellado que alguien escriba ese código, pero aún así ...

Además, como dijo Felix King, la interfaz es un poco inconsistente y podría generar algunos errores muy difíciles de rastrear.

Si desea una matriz con longitud = x, llena de indefinido (como new Array(x)lo haría), podría hacer esto:

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

alert(myArray.length); // 4
nickf
fuente
48
De acuerdo con este razonamiento, no debe usar alerty undefined, porque algún otro código podría interferir con ellos. El segundo ejemplo es menos legible que new Array(4), y no te da el mismo resultado: jsfiddle.net/73fKd
Alexey Lebedev
25
Esta respuesta es extraña
Marco Demaio
66
No puedes evitar que la gente juegue con objetos globales en JavaScript; simplemente no hagas eso ni uses marcos donde la gente haga eso.
Richard Connamacher
En la respuesta de Chrome actualmente: j = new Array (4); j.length; // resultados en 4
Parris