Eficiencia de matriz versus objeto en JavaScript
Tengo un modelo con posiblemente miles de objetos. Me preguntaba cuál sería la forma más eficaz de almacenarlos y recuperar un único objeto una vez que tenga su identificación. Las identificaciones son números largos.
Estas son las 2 opciones en las que estaba pensando. en la opción uno es una matriz simple con un índice incremental. en la opción 2 es una matriz asociativa y tal vez un objeto, si eso hace la diferencia. Mi pregunta es cuál es más eficiente, cuando principalmente necesito recuperar un solo objeto, pero a veces también los recorro y ordeno.
Opción uno con matriz no asociativa:
var a = [{id: 29938, name: 'name1'},
{id: 32994, name: 'name1'}];
function getObject(id) {
for (var i=0; i < a.length; i++) {
if (a[i].id == id)
return a[i];
}
}
Opción dos con matriz asociativa:
var a = []; // maybe {} makes a difference?
a[29938] = {id: 29938, name: 'name1'};
a[32994] = {id: 32994, name: 'name1'};
function getObject(id) {
return a[id];
}
Actualizar:
Bien, entiendo que usar una matriz en la segunda opción está fuera de discusión. Entonces, la línea de declaración, la segunda opción realmente debería ser: var a = {};
y la única pregunta es: qué funciona mejor al recuperar un objeto con una identificación determinada: una matriz o un objeto donde la identificación es la clave.
y además, ¿cambiará la respuesta si tengo que ordenar la lista muchas veces?
La versión corta: las matrices son en su mayoría más rápidas que los objetos. Pero no existe una solución 100% correcta.
Actualización 2017: prueba y resultados
var a1 = [{id: 29938, name: 'name1'}, {id: 32994, name: 'name1'}];
var a2 = [];
a2[29938] = {id: 29938, name: 'name1'};
a2[32994] = {id: 32994, name: 'name1'};
var o = {};
o['29938'] = {id: 29938, name: 'name1'};
o['32994'] = {id: 32994, name: 'name1'};
for (var f = 0; f < 2000; f++) {
var newNo = Math.floor(Math.random()*60000+10000);
if (!o[newNo.toString()]) o[newNo.toString()] = {id: newNo, name: 'test'};
if (!a2[newNo]) a2[newNo] = {id: newNo, name: 'test' };
a1.push({id: newNo, name: 'test'});
}
Publicación original - Explicación
Hay algunos conceptos erróneos en su pregunta.
No hay matrices asociativas en Javascript. Sólo matrices y objetos.
Estas son matrices:
var a1 = [1, 2, 3];
var a2 = ["a", "b", "c"];
var a3 = [];
a3[0] = "a";
a3[1] = "b";
a3[2] = "c";
Esta también es una matriz:
var a3 = [];
a3[29938] = "a";
a3[32994] = "b";
Es básicamente una matriz con agujeros, porque cada matriz tiene indexación continua. Es más lento que las matrices sin agujeros. Pero iterar manualmente a través de la matriz es aún más lento (en su mayoría).
Este es un objeto:
var a3 = {};
a3[29938] = "a";
a3[32994] = "b";
Aquí hay una prueba de rendimiento de tres posibilidades:
Matriz de búsqueda, matriz Holey y prueba de rendimiento de objetos
Una lectura excelente sobre estos temas en Smashing Magazine: Escritura rápida de JavaScript con memoria eficiente
En realidad, no es una cuestión de rendimiento en absoluto, ya que las matrices y los objetos funcionan de manera muy diferente (o al menos se supone que deben hacerlo). Las matrices tienen un índice continuo 0..n
, mientras que los objetos asignan claves arbitrarias a valores arbitrarios. Si desea proporcionar claves específicas, la única opción es un objeto. Si no te importan las claves, es una matriz.
Si intenta establecer claves arbitrarias (numéricas) en una matriz, realmente tendrá una pérdida de rendimiento , ya que, desde el punto de vista del comportamiento, la matriz completará todos los índices intermedios:
> foo = [];
[]
> foo[100] = 'a';
"a"
> foo
[undefined, undefined, undefined, ..., "a"]
(Tenga en cuenta que la matriz en realidad no contiene 99 undefined
valores, pero se comportará de esta manera ya que [se supone que está] iterando la matriz en algún momento).
Los literales de ambas opciones deberían dejar muy claro cómo se pueden utilizar:
var arr = ['foo', 'bar', 'baz']; // no keys, not even the option for it
var obj = { foo : 'bar', baz : 42 }; // associative by its very nature
Con ES6, la forma más eficaz sería utilizar un mapa.
var myMap = new Map();
myMap.set(1, 'myVal');
myMap.set(2, { catName: 'Meow', age: 3 });
myMap.get(1);
myMap.get(2);
Puede utilizar las funciones de ES6 hoy utilizando una cuña ( https://github.com/es-shims/es6-shim ).
El rendimiento variará según el navegador y el escenario. Pero aquí hay un ejemplo donde Map
tiene mayor rendimiento: https://jsperf.com/es6-map-vs-object-properties/2
REFERENCIA https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map
En NodeJS, si conoce ID
, el bucle a través de la matriz es muy lento en comparación con object[ID]
.
const uniqueString = require('unique-string');
const obj = {};
const arr = [];
var seeking;
//create data
for(var i=0;i<1000000;i++){
var getUnique = `${uniqueString()}`;
if(i===888555) seeking = getUnique;
arr.push(getUnique);
obj[getUnique] = true;
}
//retrieve item from array
console.time('arrTimer');
for(var x=0;x<arr.length;x++){
if(arr[x]===seeking){
console.log('Array result:');
console.timeEnd('arrTimer');
break;
}
}
//retrieve item from object
console.time('objTimer');
var hasKey = !!obj[seeking];
console.log('Object result:');
console.timeEnd('objTimer');
Y los resultados:
Array result:
arrTimer: 12.857ms
Object result:
objTimer: 0.051ms
Incluso si el ID de búsqueda es el primero en la matriz/objeto:
Array result:
arrTimer: 2.975ms
Object result:
objTimer: 0.068ms