Como quedó claro en la actualización 3 de esta respuesta , esta notación:
var hash = {};
hash[X]
en realidad no pica el objeto X
; en realidad solo se convierte X
en una cadena (a través de .toString()
si es un objeto, o alguna otra conversión incorporada para varios tipos primitivos) y luego mira esa cadena hacia arriba, sin cifrar, en " hash
". La igualdad de objetos tampoco se verifica: si dos objetos diferentes tienen la misma conversión de cadena, simplemente se sobrescribirán entre sí.
Dado esto, ¿hay implementaciones eficientes de hashmaps en javascript? (Por ejemplo, el segundo resultado de Google javascript hashmap
produce una implementación que es O (n) para cualquier operación. Varios otros resultados ignoran el hecho de que diferentes objetos con representaciones de cadenas equivalentes se sobrescriben entre sí.
Respuestas:
¿Por qué no hash hash tus objetos de forma manual, y utilizar las cadenas resultantes como claves para un diccionario JavaScript normal? Después de todo, usted está en la mejor posición para saber qué hace que sus objetos sean únicos. Eso es lo que hago.
Ejemplo:
De esta forma, puede controlar la indexación realizada por JavaScript sin una gran carga de asignación de memoria y manejo de desbordamiento.
Por supuesto, si realmente desea la "solución de grado industrial", puede crear una clase parametrizada por la función clave y con todas las API necesarias del contenedor, pero ... usamos JavaScript y tratamos de ser simples y livianos, así que Esta solución funcional es simple y rápida.
La función de la tecla puede ser tan simple como seleccionar los atributos correctos del objeto, por ejemplo, una tecla o un conjunto de teclas, que ya son únicas, una combinación de teclas, que son únicas juntas, o tan complejas como usar algunos hashes criptográficos como en codificación DojoX o DojoX UUID . Si bien las últimas soluciones pueden producir claves únicas, personalmente trato de evitarlas a toda costa, especialmente si sé qué hace que mis objetos sean únicos.
Actualización en 2014: respondida en 2008, esta solución simple todavía requiere más explicaciones. Permítanme aclarar la idea en forma de preguntas y respuestas.
Su solución no tiene un verdadero hash. ¿¿¿Dónde está???
JavaScript es un lenguaje de alto nivel. Su primitivo básico ( Objeto ) incluye una tabla hash para mantener las propiedades. Esta tabla hash generalmente se escribe en un lenguaje de bajo nivel para mayor eficiencia. Usando un objeto simple con teclas de cadena, usamos una tabla hash implementada eficientemente sin ningún esfuerzo de nuestra parte.
¿Cómo sabes que usan hash?
Hay tres formas principales de mantener una colección de objetos direccionables por una clave:
Obviamente, los objetos JavaScript usan tablas hash de alguna forma para manejar casos generales.
¿Los vendedores de navegadores realmente usan tablas hash?
De Verdad.
¿Manejan colisiones?
Si. Véase más arriba. Si encuentra una colisión en cadenas desiguales, no dude en presentar un error a un proveedor.
Entonces, ¿cuál es tu idea?
Si desea hacer un hash de un objeto, encuentre lo que lo hace único y úselo como clave. No intente calcular hash real o emular tablas hash: el objeto JavaScript subyacente ya lo maneja de manera eficiente.
Use esta clave con JavaScript
Object
para aprovechar su tabla hash incorporada y evitar posibles conflictos con las propiedades predeterminadas.Ejemplos para comenzar:
Usé su sugerencia y almacené en caché todos los objetos con un nombre de usuario. ¡Pero un tipo sabio se llama "toString", que es una propiedad incorporada! ¿Qué debería hacer ahora?
Obviamente, si es remotamente posible que la clave resultante consista exclusivamente en caracteres latinos, debe hacer algo al respecto. Por ejemplo, agregue cualquier carácter Unicode no latino que le guste al principio o al final para desempaquetar con las propiedades predeterminadas: "#toString", "#MarySmith". Si se usa una clave compuesta, separe los componentes clave utilizando algún tipo de delimitador no latino: "nombre, ciudad, estado".
En general, este es el lugar, donde tenemos que ser creativos y seleccionar las teclas más fáciles con limitaciones dadas (singularidad, posibles conflictos con las propiedades predeterminadas).
Nota: las claves únicas no entran en conflicto por definición, mientras que los posibles conflictos de hash serán manejados por el subyacente
Object
.¿Por qué no te gustan las soluciones industriales?
En mi humilde opinión, el mejor código es ningún código: no tiene errores, no requiere mantenimiento, es fácil de entender y se ejecuta instantáneamente. Todas las "tablas hash en JavaScript" que vi eran> 100 líneas de código e involucraban múltiples objetos. Compararlo con:
dict[key] = value
.Otro punto: ¿es posible incluso superar el rendimiento de un objeto primordial escrito en un lenguaje de bajo nivel, usando JavaScript y los mismos objetos primordiales para implementar lo que ya está implementado?
¡Todavía quiero cortar mis objetos sin ninguna llave!
Estamos de suerte: ECMAScript 6 (programado para mediados de 2015, más o menos 1-2 años después de que se generalice) define el mapa y el conjunto .
A juzgar por la definición, pueden usar la dirección del objeto como clave, lo que hace que los objetos se distingan instantáneamente sin claves artificiales. OTOH, dos objetos diferentes, pero idénticos, se asignarán como distintos.
Desglose de comparación de MDN :
fuente
Descripción del problema
JavaScript no tiene un tipo de mapa general incorporado (a veces llamado matriz asociativa o diccionario ) que permite acceder a valores arbitrarios mediante claves arbitrarias. La estructura de datos fundamental de JavaScript es el objeto , un tipo especial de mapa que solo acepta cadenas como claves y tiene una semántica especial como herencia prototípica, captadores y establecedores y algo más de vudú.
Cuando utilice objetos como mapas, debe recordar que la clave se convertirá en un valor de cadena a través de
toString()
, lo que da como resultado la asignación5
y'5'
al mismo valor y todos los objetos que no sobrescriben eltoString()
método al valor indexado por'[object Object]'
. También puede acceder involuntariamente a sus propiedades heredadas si no marcahasOwnProperty()
.El tipo de matriz incorporado de JavaScript no ayuda un bit: las matrices de JavaScript no son matrices asociativas, sino solo objetos con algunas propiedades especiales más. Si quieres saber por qué no se pueden usar como mapas, mira aquí .
La solución de Eugene
Eugene Lazutkin ya describió la idea básica de usar una función hash personalizada para generar cadenas únicas que se pueden usar para buscar los valores asociados como propiedades de un objeto de diccionario. Esta será probablemente la solución más rápida, porque los objetos se implementan internamente como tablas hash .
Para obtener un valor hash único para objetos arbitrarios, una posibilidad es usar un contador global y almacenar en caché el valor hash en el objeto mismo (por ejemplo, en una propiedad denominada
__hash
).Una función hash que hace esto y funciona tanto para valores primitivos como para objetos es:
Esta función se puede usar como lo describe Eugene. Por conveniencia, lo envolveremos en una
Map
clase.Mi
Map
implementacionLa siguiente implementación almacenará adicionalmente los pares clave-valor en una lista doblemente vinculada para permitir una iteración rápida sobre claves y valores. Para proporcionar su propia función hash, puede sobrescribir el
hash()
método de la instancia después de la creación.Ejemplo
El siguiente script
genera esta salida:
Consideraciones adicionales
PEZ sugirió sobrescribir el
toString()
método, presumiblemente con nuestra función hash. Esto no es factible porque no funciona para valores primitivos (cambiartoString()
por primitivos es una muy mala idea). Si queremostoString()
devolver valores significativos para objetos arbitrarios, tendríamos que modificarObject.prototype
, lo que algunas personas (yo no incluido) consideran verboten .Editar: la versión actual de mi
Map
implementación, así como otras ventajas de JavaScript, se pueden obtener desde aquí .fuente
Map._counter = 0
, y en el constructor de mapas hacerthis._hash = 'object ' + Map._counter++
. Luego de hash () se convierte enreturn (value && value._hash) || (typeof(value) + ' ' + String(value));
Sé que esta pregunta es bastante antigua, pero hay algunas soluciones realmente buenas hoy en día con bibliotecas externas.
JavaScript también tiene su lenguaje proporcionado
Map
también.fuente
Aquí hay una manera fácil y conveniente de usar algo similar al mapa de Java:
Y para obtener el valor:
fuente
Según el estándar ECMAScript 2015 (ES6), JavaScript tiene una implementación de mapa. Más sobre cuál se puede encontrar aquí
Uso básico:
fuente
Puede usar ES6
WeakMap
oMap
:Tenga en cuenta que ninguno de los dos es ampliamente compatible, pero puede usar ES6 Shim (requiere ES5 nativo o ES5 Shim ) para admitirlo
Map
, pero noWeakMap
( vea por qué ).fuente
Tendría que almacenar en algunos acoplamientos de estado interno de pares de objeto / valor
Y úsalo como tal:
Por supuesto, esta implementación también está en algún lugar a lo largo de las líneas de O (n). Los ejemplos anteriores de Eugene son la única forma de obtener un hash que funcione con cualquier tipo de velocidad que esperarías de un hash real.
Actualizar:
Otro enfoque, en la línea de la respuesta de Eugene es de alguna manera adjuntar una identificación única a todos los objetos. Uno de mis enfoques favoritos es tomar uno de los métodos incorporados heredados de la superclase Object, reemplazarlo con un paso de función personalizado y adjuntar propiedades a ese objeto de función. Si tuviera que reescribir mi método HashMap para hacer esto, se vería así:
Esta versión parece ser solo un poco más rápida, pero en teoría será significativamente más rápida para grandes conjuntos de datos.
fuente
Desafortunadamente, ninguna de las respuestas anteriores fue buena para mi caso: diferentes objetos clave pueden tener el mismo código hash. Por lo tanto, escribí una versión simple de HashMap similar a Java:
Nota: los objetos clave deben "implementar" los métodos hashCode () y equals ().
fuente
new Array()
over[]
es asegurar la absoluta similitud de Java de su código? :)He implementado un JavaScript HashMap cuyo código se puede obtener de http://github.com/lambder/HashMapJS/tree/master
Aquí está el código:
fuente
HashMap
electrónicos.En ECMA6 puedes usar WeakMap
Ejemplo:
Pero:
fuente
Javascript no incorpora Map / hashmap. Debería llamarse matriz asociativa .
hash["X"]
es igual ahash.X
, pero permite "X" como una variable de cadena. En otras palabras,hash[x]
es funcionalmente igual aeval("hash."+x.toString())
Es más similar a object.properties que al mapeo de valores clave. Si está buscando una mejor asignación de clave / valor en Javascript, utilice el objeto Map que puede encontrar en la web.
fuente
Pruebe la implementación de mi tabla hash de JavaScript: http://www.timdown.co.uk/jshashtable
Busca un método hashCode () de objetos clave, o puede proporcionar una función hash al crear un objeto Hashtable.
fuente
Esto parece una solución bastante robusta: https://github.com/flesler/hashmap . Incluso funcionará bien para funciones y objetos que se ven idénticos. El único truco que usa es agregar un miembro oscuro a un objeto para identificarlo. Si su programa no sobrescribe esa variable oscura (es algo así como hashid ), está dorado.
fuente
Si el rendimiento no es crítico (por ejemplo, la cantidad de teclas es relativamente pequeña) y no quieren contaminar su (o tal vez no su) objetos con campos adicionales como
_hash
,_id
, etc., entonces usted puede hacer uso del hecho de queArray.prototype.indexOf
emplea estricta igualdad Aquí hay una implementación simple:Ejemplo de uso:
En comparación con ES6 WeakMap, tiene dos problemas: O (n) tiempo de búsqueda y falta de debilidad (es decir, provocará una pérdida de memoria si no usa
delete
oclear
suelta las teclas).fuente
My Map Implementation, derivado del ejemplo de Christoph:
Ejemplo de uso:
fuente
Agregando otra solución:
HashMap
es prácticamente la primera clase que porté de Java a Javascript. Se podría decir que hay mucha sobrecarga, pero la implementación es casi 100% igual a la implementación de Java e incluye todas las interfaces y subclases.El proyecto se puede encontrar aquí: https://github.com/Airblader/jsava También adjuntaré el código fuente (actual) para la clase HashMap, pero como se indicó también depende de la superclase, etc. El marco de OOP utilizado es qooxdoo.
Editar: tenga en cuenta que este código ya está desactualizado y consulte el proyecto github para el trabajo actual. Al escribir esto, también hay una
ArrayList
implementación.fuente
Sin embargo, otra implementación del mapa por mí. Con randomizer, 'generics' e 'iterator' =)
Ejemplo:
fuente