¿Cuál es la forma más eficiente de crear una matriz arbitraria de longitud cero en JavaScript?
javascript
arrays
dil
fuente
fuente
let i = 0; Array.from(Array(10), ()=>i++);
Respuestas:
ES6 presenta
Array.prototype.fill
. Se puede usar así:No estoy seguro si es rápido, pero me gusta porque es breve y se describe a sí mismo.
Todavía no está en IE ( verifique la compatibilidad ), pero hay un polyfill disponible .
fuente
new Array(len)
es dolorosamente lento(arr = []).length = len; arr.fill(0);
se trata de la solución más rápida que he visto en cualquier lugar ... o al menos atadoarr = Array(n)
y se(arr = []).length = n
comportan de manera idéntica de acuerdo con las especificaciones. En algunas implementaciones, uno podría ser más rápido, pero no creo que haya una gran diferencia.(arr = []).length = 1000;
contra laarr = new Array(1000);
velocidad, pruébelo tanto en Chrome como en FF ...new
es terriblemente lento. Ahora, para longitudes de matriz más pequeñas ... digamos <50 o más o menos ... entoncesnew Array()
parece funcionar mejor. Pero ..arr.fill(0)
... todo cambia. Ahora, el usonew Array()
es más rápido en la mayoría de los casos, excepto cuando llega a tamaños de matriz> 100000 ... Entonces puede comenzar a ver que la velocidad aumenta nuevamente. Pero si en realidad no tiene que rellenarlo con ceros y puede usar falisy estándar de matrices vacías. Entonces(arr = []).length = x
es una locura rápida en mis casos de prueba la mayor parte del tiempo.new Array(5).forEach(val => console.log('hi'));
vsnew Array(5).fill(undefined).forEach(val => console.log('hi'));
.Aunque este es un hilo viejo, quería agregarle mis 2 centavos. No estoy seguro de lo lento / rápido que es esto, pero es rápido. Esto es lo que hago:
Si quiero rellenar previamente con un número:
Si quiero rellenar previamente con una cadena:
Otras respuestas han sugerido:
pero si quieres 0 (el número) y no "0" (cero dentro de una cadena), puedes hacer:
fuente
Array.apply(null, new Array(5)).map(...)
? Porque simplemente hacer (nueva matriz (5)). Map (...) no funcionará como dice la especificaciónnew
) Cuando lo hacesArray(5)
, estás creando un objeto que se parece a esto:{ length: 5, __proto__: Array.prototype }
- inténtaloconsole.dir( Array(5) )
. Tenga en cuenta que no tiene ningún propiedades0
,1
,2
, etc, pero cuandoapply
que hasta elArray
constructor, que es como decirArray(undefined, undefined, undefined, undefined, undefined)
. Y obtienes un objeto que se parece un poco{ length: 5, 0: undefined, 1: undefined...}
.map
funciona en las propiedades0
,1
etc., por eso su ejemplo no funciona, pero cuando lo usaapply
sí lo hace..apply
es en realidad lo que quieresthis
que sea. Para estos finesthis
, no importa, solo nos importa realmente la "característica" de difusión de parámetros de.apply
, por lo que puede ser cualquier valor. Me gustanull
porque es barato, probablemente no quieras usarlo{}
o[]
porque estarías creando una instancia de un objeto sin ningún motivo.Forma elegante de llenar una matriz con valores calculados previamente
Aquí hay otra forma de hacerlo usando ES6 que nadie ha mencionado hasta ahora:
Funciona pasando una función de mapa como el segundo parámetro de
Array.from
.En el ejemplo anterior, el primer parámetro asigna una matriz de 3 posiciones llenas con el valor
undefined
y luego la función lambda asigna cada una de ellas al valor0
.Aunque
Array(len).fill(0)
es más corto, no funciona si necesita completar la matriz haciendo un cálculo primero (sé que la pregunta no lo hizo, pero mucha gente termina buscando esto) .Por ejemplo, si necesita una matriz con 10 números aleatorios:
Es más conciso (y elegante) que el equivalente:
Este método también se puede utilizar para generar secuencias de números aprovechando el parámetro de índice proporcionado en la devolución de llamada:
Respuesta extra: llenar una matriz usando String
repeat()
Como esta respuesta está recibiendo mucha atención, también quería mostrar este truco genial. Aunque no es tan útil como mi respuesta principal, presentaré el
repeat()
método String aún no muy conocido pero muy útil . Aquí está el truco:Genial, ¿eh?
repeat()
es un método muy útil para crear una cadena que es la repetición de la cadena original un cierto número de veces. Después de eso,split()
crea una matriz para nosotros, que luego esmap()
pedida a los valores que queremos. Desglosándolo en pasos:fuente
repeat
truco definitivamente no es deseado en la producción,Array.from()
está perfectamente bien :-)En breve
La solución más rápida
let a = new Array(n); for (let i=0; i<n; ++i) a[i] = 0;
La solución más corta (práctica) (3 veces más lenta para matrices pequeñas, ligeramente más lenta para grandes (más lenta en Firefox))
Array(n).fill(0)
Detalles
Hoy 2020.06.09 realizo pruebas en macOS High Sierra 10.13.6 en los navegadores Chrome 83.0, Firefox 77.0 y Safari 13.1. Pruebo las soluciones elegidas para dos casos de prueba
Conclusiones
new Array(n)+for
(N) es la solución más rápida para matrices pequeñas y matrices grandes (excepto Chrome, pero todavía es muy rápida allí) y se recomienda como solución rápida de navegador cruzadonew Float32Array(n)
(I) devuelve una matriz no típica (p. ej., no se puede llamarpush(..)
), por lo que no comparo sus resultados con otras soluciones; sin embargo, esta solución es aproximadamente 10-20 veces más rápida que otras soluciones para matrices grandes en todos los navegadoresfor
(L, M, N, O) son rápidas para arreglos pequeñosfill
(B, C) son rápidas en Chrome y Safari pero sorprendentemente más lentas en Firefox para grandes matrices. Son medianamente rápidos para matrices pequeñas.Array.apply
(P) arroja error para grandes matricesMostrar fragmento de código
Código y ejemplo
El siguiente código presenta soluciones utilizadas en mediciones
Mostrar fragmento de código
Resultados de ejemplo para Chrome
fuente
let a=[]; for(i=n;i--;) a.push(0);
, pero es 4 veces más lento quefill(0)
, por lo que ni siquiera actualizaré la imagen de ese caso.El método de llenado ES 6 ya mencionado se encarga de esto muy bien. La mayoría de los navegadores de escritorio modernos ya son compatibles con los métodos prototipo de matriz requeridos a partir de hoy (Chromium, FF, Edge y Safari) [ 1 ]. Puede buscar detalles en MDN . Un ejemplo de uso simple es
Dado el soporte actual del navegador, debe tener cuidado de usarlo a menos que esté seguro de que su audiencia usa navegadores de escritorio modernos.
fuente
a = Array(10).fill(null).map(() => { return []; });
a = Array(10).fill(0).map( _ => [] );
Nota agregada en agosto de 2013, actualizada en febrero de 2015: la respuesta a continuación de 2009 se refiere al
Array
tipo genérico de JavaScript . No se relaciona con las matrices tipadas más nuevas definidas en ES2015 [y disponibles ahora en muchos navegadores], me gustaInt32Array
y tal. También tenga en cuenta que ES2015 agrega unfill
método para ambas matrices y matrices con tipo , que es probable que sea la forma más eficiente para llenarlos ...Además, puede hacer una gran diferencia para algunas implementaciones en la forma de crear la matriz. El motor V8 de Chrome, en particular, intenta usar una matriz de memoria contigua altamente eficiente si cree que puede hacerlo, cambiando a la matriz basada en objetos solo cuando es necesario.
Con la mayoría de los idiomas, sería preasignar, luego rellenar con cero, así:
Pero , las matrices de JavaScript no son realmente matrices , son mapas de clave / valor al igual que todos los demás objetos de JavaScript, por lo que no hay que "preasignar" (establecer la longitud no asigna tantas ranuras para llenar), ni ¿Hay alguna razón para creer que el beneficio de contar regresivamente a cero (que es solo para hacer una comparación rápida en el bucle) no se supera al agregar las claves en orden inverso cuando la implementación puede haber optimizado su manejo de las claves relacionado con las matrices sobre la teoría, generalmente las hará en orden.
De hecho, Matthew Crumley señaló que la cuenta regresiva es notablemente más lenta en Firefox que la cuenta regresiva, un resultado que puedo confirmar: es la parte de la matriz (el bucle descendente a cero es aún más rápido que el bucle hasta un límite en una var). Aparentemente, agregar los elementos a la matriz en orden inverso es una operación lenta en Firefox. De hecho, los resultados varían bastante según la implementación de JavaScript (que no es tan sorprendente). Aquí hay una página de prueba rápida y sucia (a continuación) para las implementaciones del navegador (muy sucia, no rinde durante las pruebas, por lo que proporciona una respuesta mínima y va en contra de los límites de tiempo del script) Recomiendo refrescarse entre pruebas; FF (al menos) se ralentiza en las pruebas repetidas si no lo hace.
La versión bastante complicada que usa Array # concat es más rápida que un init directo en FF a partir de entre 1,000 y 2,000 arreglos de elementos. Sin embargo, en el motor V8 de Chrome, el init directo gana cada vez ...
Aquí está la página de prueba ( copia en vivo ):
fuente
Por defecto
Uint8Array
,Uint16Array
y lasUint32Array
clases mantienen ceros como sus valores, por lo que no necesita ninguna técnica de relleno compleja, simplemente haga lo siguiente:Todos los elementos de la matriz
ary
serán ceros por defecto.fuente
Array.isArray(ary)
esfalse
. La longitud también es de sólo lectura por lo que no se puede empujar nuevos elementos a ella al igual que conary.push
0
como su valor predeterminado.Array.from(new Uint8Array(10))
proporcionará una matriz normal.Array(n).fill(0)
en Chrome si lo que realmente necesita es una matriz JS. Sin embargo, si puede usar un TypedArray, esto es mucho más rápido.fill(0)
, especialmente si puede usar el valor inicializador predeterminado de0
. No parece haber un constructor que tome un valor de relleno y una longitud, como lo hizo C ++std::vector
. Parece que para cualquier valor distinto de cero, tiene que construir un TypedArray a cero y luego llenarlo. : /Si usa ES6, puede usar Array.from () de esta manera:
Tiene el mismo resultado que
Porque
fuente
Tenga en cuenta que
while
es por lo general más eficientes quefor-in
,forEach
, etc.fuente
i
extraña la variable local?length
se pasa por valor, por lo que debería poder disminuirlo directamente.arr[i] = value
). Es mucho más rápido recorrer de principio a fin y usararr.push(value)
. Es molesto, porque prefiero tu método.usando notación de objeto
cero lleno? me gusta...
lleno de 'indefinido' ...
notación obj con ceros
Como nota al margen, si modifica el prototipo de Array, ambos
y
tendrá esas modificaciones prototipo
En cualquier caso, no me preocuparía demasiado la eficiencia o la velocidad de esta operación, hay muchas otras cosas que probablemente harás que son mucho más derrochadoras y costosas que instanciar una variedad de longitud arbitraria que contiene ceros.
fuente
null
var x = new Array(7);
new Array(7)
no no crear una matriz "lleno indefinido". Crea una matriz vacía con longitud 7.(new Array(10)).fill(0)
.He probado todas las combinaciones de preasignación / no preasignación, conteo ascendente / descendente y bucles for / while en IE 6/7/8, Firefox 3.5, Chrome y Opera.
Las funciones a continuación fueron consistentemente las más rápidas o extremadamente cercanas en Firefox, Chrome e IE8, y no mucho más lentas que las más rápidas en Opera e IE 6. También es la más simple y clara en mi opinión. He encontrado varios navegadores donde la versión del bucle while es un poco más rápida, por lo que también la incluyo como referencia.
o
fuente
var array = []
declaración en la primera parte del bucle for en realidad, separada por solo una coma.length
valor dado para que no cambie constantemente. Trají una matriz de ceros de 1 millón de longitud de 40ms a 8 en mi máquina.for (i = 0, array = []; i < length; ++i) array[i] = val;
¿Menos bloques? ... de todos modos, también ... si configuro laarray.length
nueva matriz a la longitud ... parece que obtengo otro aumento de velocidad del 10% -15% en FF ... en Chrome, parece duplicar la velocidad ->var i, array = []; array.length = length; while(i < length) array[i++] = val;
(todavía era más rápido si lo dejaba como unfor
bucle ... pero el init ya no es necesario, por lowhile
que parece ser más rápido en esta versión)Si necesita crear muchas matrices llenas de cero de diferentes longitudes durante la ejecución de su código, la forma más rápida que he encontrado para lograr esto es crear una matriz cero una vez , usando uno de los métodos mencionados en este tema, de una longitud que sabe que nunca se excederá, y luego corte esa matriz según sea necesario.
Por ejemplo (usando la función de la respuesta elegida arriba para inicializar la matriz), cree una matriz llena de cero de longitud maxLength , como una variable visible para el código que necesita cero matrices:
Ahora corte esta matriz cada vez que necesite una matriz llena de ceros de longitud requeridaLongitud < maxLongitud :
Creé miles de arrays llenos miles de veces durante la ejecución de mi código, lo que aceleró enormemente el proceso.
fuente
fuente
new Array(size+1).join("x").split("x").map(function() { return 0; })
para obtener números realesnew Array(size+1).join('0').split('').map(Number)
No tengo nada en contra:
sugerido por Zertosh, pero en una nueva matriz de ES6, las extensiones le permiten hacer esto de forma nativa con el
fill
método. Ahora IE edge, Chrome y FF lo admiten, pero consulte la tabla de compatibilidadnew Array(3).fill(0)
le dará[0, 0, 0]
. Puede llenar la matriz con cualquier valor comonew Array(5).fill('abc')
(incluso objetos y otras matrices).Además de eso, puede modificar matrices anteriores con relleno:
que te da:
[1, 2, 3, 9, 9, 6]
fuente
La forma en que generalmente lo hago (y es increíblemente rápido) es usar
Uint8Array
. Por ejemplo, crear un vector lleno de cero de elementos 1M:Soy un usuario de Linux y siempre he trabajado para mí, pero una vez que un amigo que usaba una Mac tenía algunos elementos distintos de cero. Pensé que su máquina no funcionaba bien, pero aún así esta es la forma más segura que encontramos para solucionarlo:
Editado
Chrome 25.0.1364.160
Firefox 20.0
Falta la prueba más importante (al menos para mí): la Node.js. Sospecho que está cerca del punto de referencia de Chrome.
fuente
Usando lodash o guión bajo
O si tiene una matriz existente y desea una matriz de la misma longitud
fuente
_.range(0, length, 0)
, creo. Lodash es exclusivo del valor finalSolución ES6:
fuente
A partir de ECMAScript2016 , existe una opción clara para matrices grandes.
Dado que esta respuesta todavía aparece cerca de la parte superior en las búsquedas de Google, aquí hay una respuesta para 2017.
Aquí hay un jsbench actual con algunas docenas de métodos populares, incluidos muchos propuestos hasta ahora sobre esta pregunta. Si encuentra un método mejor, agregue, bifurque y comparta.
Quiero señalar que no existe una forma más eficiente de crear una matriz arbitraria de longitud cero. Puede optimizar la velocidad o la claridad y la facilidad de mantenimiento; puede considerarse la opción más eficiente según las necesidades del proyecto.
Al optimizar la velocidad, desea: crear la matriz usando sintaxis literal; establece la longitud, inicializa la variable iterativa e itera a través de la matriz usando un ciclo while. Aquí hay un ejemplo.
Otra posible implementación sería:
Pero desaconsejo encarecidamente usar esta segunda implantación en la práctica, ya que es menos clara y no le permite mantener el alcance del bloque en su variable de matriz.
Estos son significativamente más rápidos que el llenado con un bucle for, y aproximadamente un 90% más rápido que el método estándar de
Pero este método de relleno sigue siendo la opción más eficiente para matrices más pequeñas debido a su claridad, concisión y facilidad de mantenimiento. La diferencia de rendimiento probablemente no lo matará a menos que esté haciendo muchos arreglos con longitudes del orden de miles o más.
Algunas otras notas importantes. La mayoría de las guías de estilo recomiendan que ya no se use
var
sin una razón muy especial cuando se usa ES6 o posterior. Úseloconst
para variables que no se redefinirán ylet
para variables que sí lo serán. El MDN y la Guía de estilo de Airbnb son excelentes lugares para obtener más información sobre las mejores prácticas. Las preguntas no se referían a la sintaxis, pero es importante que las personas nuevas en JS conozcan estos nuevos estándares cuando busquen en estas resmas de respuestas antiguas y nuevas.fuente
Para crear una matriz completamente nueva
Para agregar algunos valores al final de una matriz existente
[...existingArray, ...new Array(numberOfElementsToAdd).fill(0)]
Ejemplo
fuente
No vi este método en las respuestas, así que aquí está:
Como resultado, obtendrá una matriz de longitud 200 de valor cero:
No estoy seguro del rendimiento de este código, pero no debería ser un problema si lo usa para arreglos relativamente pequeños.
fuente
fuente
Esta
concat
versión es mucho más rápida en mis pruebas en Chrome (2013-03-21). Aproximadamente 200 ms para 10,000,000 elementos vs 675 para init directo.Bonificación: si desea llenar su matriz con cadenas, esta es una forma concisa de hacerlo (no tan rápido como
concat
si fuera):fuente
Estaba probando la gran respuesta de TJ Crowder, y se me ocurrió una fusión recursiva basada en la solución concat que supera a cualquiera de sus pruebas en Chrome (no probé otros navegadores).
llama al método con
makeRec(29)
.fuente
¿Qué hay de
new Array(51).join('0').split('')
?fuente
.map(function(a){return +a})
?Vale la pena señalar que se
Array.prototype.fill
había agregado como parte de la propuesta ECMAScript 6 (Armonía) . Prefiero ir con el polyfill escrito a continuación, antes de considerar otras opciones mencionadas en el hilo.fuente
El más corto para el código de bucle
Versión var segura
fuente
n
, esta sería más corta:for(var a=[];n--;a[n]=0);
fuente
Mi función más rápida sería:
Usar el método push y shift nativo para agregar elementos a la matriz es mucho más rápido (aproximadamente 10 veces) que declarar el alcance de la matriz y hacer referencia a cada elemento para establecer su valor.
FYI: constantemente obtengo tiempos más rápidos con el primer ciclo, que es una cuenta regresiva, cuando ejecuto esto en firebug (extensión de firefox).
¿Me interesa saber qué hace TJ Crowder con eso? :-)
fuente
while (len--)
... tomó mis tiempos de procesamiento de aproximadamente 60 ms a aproximadamente 54 msSabía que tenía este protocolo en alguna parte :)
Editar: pruebas
En respuesta a Joshua y otros métodos, realicé mi propia evaluación comparativa y estoy viendo resultados completamente diferentes a los reportados.
Esto es lo que probé:
Resultados:
Entonces, según mi cálculo, en realidad es más lento en general, pero funciona mejor con matrices más largas en FF pero peor en IE, que en general es una mierda (sorpresa).
fuente
b = []...
) es 10-15% más rápido que el primero, pero es más de 10 veces más lento que la respuesta de Joshua.else {this.length=n;}
después de lathis.length
comprobación. Esto acortará una matriz ya existente si es necesario al volver ainit
ponerla en una longitud diferenten
.Función anónima:
Un poco más corto con for-loop:
Funciona con cualquiera
Object
, solo cambia lo que hay dentrothis.push()
.Incluso puedes guardar la función:
Llámalo usando:
Agregar elementos a una matriz ya existente:
Rendimiento: http://jsperf.com/zero-filled-array-creation/25
fuente