2009-07-02 19 views
36

¿puede una matriz en JS ser asociativa Y indexada? Me gustaría poder buscar un elemento en la matriz por su posición o por un valor de clave ... posible?javascript array Y asociativo indexado?

+0

esto es bastante viejo, pero se puede utilizar Object.keys (matriz) para obtener las claves con el fin –

+1

usted es un programador PHP :) – nawfal

Respuesta

6

El orden en que aparecen los objetos en una matriz javascript asociativa no está definido, y será diferente en las diferentes implementaciones. Por esa razón, no se puede contar realmente con una clave asociativa dada para estar siempre en el mismo índice.

EDIT:

como señala Perspx, no son realmente verdaderas matrices asociativas en JavaScript. La declaración foo["bar"] es sólo azúcar sintáctica para foo.bar

Si confía en el navegador para mantener el orden de los elementos en un objeto, se podría escribir una función

function valueForIndex(obj, index) { 

    var i = 0; 
    for (var key in obj) { 

     if (i++ == index) 
      return obj[key]; 
    } 
} 
+3

Aunque se trata de una verdadera, en la práctica, todos los principales navegadores recorren las propiedades de un objeto en el orden en que se definieron. Esto no está en la especificación, por supuesto, pero vale la pena mencionarlo. –

+0

+1 ¡Su sugerencia es el método más rápido! – mate64

26

No hay cosas tales como matrices asociativas en Javascript . Puede usar literales de objeto, que parecen como matrices asociativas, pero tienen propiedades desordenadas. Los conjuntos de JavaScript normales se basan en índices enteros y no pueden ser asociativos.

Por ejemplo, con este objeto:

var params = { 
    foo: 1, 
    bar: 0, 
    other: 2 
}; 

Puede acceder a las propiedades del objeto, por ejemplo:

params["foo"]; 

Y también se puede iterar sobre el objeto con la declaración for...in:

for(var v in params) { 
    //v is equal to the currently iterated property 
} 

Sin embargo, no hay una regla estricta sobre el orden de la propiedad iteración: dos iteraciones de su objeto literal podrían devolver las propiedades en diferentes órdenes.

+1

En realidad, las matrices de JavaScript también pueden tener índices no enteros. Simplemente no tiene mucho en el camino de los métodos para tratar con ellos. – Nosredna

+3

¿Te refieres a una cadena? en ese caso, no es una matriz, solo un objeto con propiedades. – roryf

+3

Nosredna: las matrices JS no tienen índices clave, esos objetos JS se consideran literales de objeto. so: foo ["bar"] = 1; foo.bar === foo ["barra"]; // true foo ["bar"] === foo [0]; // falso Esta es una de las muchas peculiaridades sutiles de JS que desanima a la gente fácilmente. – linusthe3rd

20

Después de leer el Wikipedia definition of associative array, voy a romper con el conocimiento tradicional de JavaScript y decir "sí, JavaScript tiene matrices asociativas". Con las matrices de JavaScript, puede agregar, reasignar, eliminar y buscar valores por sus claves (y las claves pueden ser cadenas entrecomilladas), que es lo que Wikipedia dice que las matrices asociativas deberían ser capaces de hacer.

Sin embargo, parece que se está preguntando algo diferente: si puede buscar el mismo valor por índice o clave. Eso no es un requisito de matrices asociativas (vea el artículo de Wikipedia). Las matrices asociativas no tienen que darle la capacidad de obtener un valor por índice.

JavaScript arrays are very closely akin to JavaScript objects.

arr=[]; 
    arr[0]="zero"; 
    arr[1]="one"; 
    arr[2]="two"; 
    arr["fancy"]="what?"; 

Sí, eso es una matriz, y sí, puede salirse con índices no numéricos. (Si tiene curiosidad, después de todo esto, arr.length es 3.)

En la mayoría de los casos, creo que debe mantener los índices numéricos cuando utiliza matrices. Eso es lo que la mayoría de los programadores esperan, creo.

El enlace es a la publicación de mi blog sobre el tema.

+0

Tenías razón cuando dijiste "Y puedes tener una matriz con índices no numéricos". Se me pasó por la cabeza de alguna manera, pero sé este hecho. –

+13

Para ser pedante, "fantasía" no es un índice en la matriz, sino un atributo del objeto instancia de la matriz. – BaroqueBobcat

+0

Sí, buen punto. Es por eso que digo que las matrices están estrechamente relacionadas con objetos en JavaScript. – Nosredna

9

Los objetos nativos JS solo aceptan cadenas como nombres de propiedad, lo cual es verdadero even for numeric array indices; las matrices difieren de los objetos normales solo en que la mayoría de las implementaciones JS almacenan las propiedades indexadas numéricamente de manera diferente (es decir, en una matriz real siempre que sean densas) y su configuración desencadenará operaciones adicionales (por ejemplo, ajuste de la propiedad length).

Si está buscando un mapa que acepte claves arbitrarias, tendrá que usar un non-native implementation. La secuencia de comandos está pensada para una iteración rápida y no de acceso aleatorio por índices numéricos, por lo que podría no ser lo que estás buscando.

Una aplicación de barebones de un mapa que haría lo que estás pidiendo podría tener este aspecto:

function Map() { 
    this.length = 0; 
    this.store = {}; 
} 

Map.prototype.get = function(key) { 
    return this.store.hasOwnProperty(key) ? 
     this.store[key] : undefined; 
}; 

Map.prototype.put = function(key, value, index) { 
    if(arguments.length < 3) { 
     if(this.store.hasOwnProperty(key)) { 
      this.store[key].value = value; 
      return this; 
     } 

     index = this.length; 
    } 
    else if(index >>> 0 !== index || index >= 0xffffffff) 
     throw new Error('illegal index argument'); 

    if(index >= this.length) 
     this.length = index + 1; 

    this[index] = this.store[key] = 
     { index : index, key : key, value : value }; 

    return this; 
}; 

El argumento de indexput() es opcional.

Se puede acceder a los valores en un mapa map ya sea por el índice o clave a través de

map.get('key').value 
map[2].value 
+0

+1, pero perdería "las propiedades indexadas numéricamente se almacenarán de forma diferente": ese no es necesariamente el caso, y la especificación no lo exige. – Miles

+0

@Miles: [x] hecho – Christoph

+0

¿Cómo se pone? map.put ('clave', 'valor') y mapa [2] .value = val? – Chris

1
var stuff = []; 
stuff[0] = "foo"; 
stuff.bar = stuff[0]; // stuff.bar can be stuff["bar"] if you prefer 
var key = "bar"; 
alert(stuff[0] + ", " + stuff[key]); // shows "foo, foo" 
+0

pero entonces, stuff.length ya no sería 1 porque has agregado .bar ¿verdad? por lo tanto, pasar por el índice realmente no funcionaría más. – puffpio

+1

No: agregar una propiedad con nombre no aumenta la longitud; solo agregar un elemento por índice numérico aumenta la duración. Agregar "cosas [1000] = 'blah'" hará que la duración aumente a 1001, aunque solo se hayan agregado dos elementos indexados numéricamente y una propiedad con nombre. Diversión no lo es ;-) – NickFitz

-2

Sí.

test = new Array(); 
test[0] = 'yellow'; 
test['banana'] = 0; 
alert(test[test['banana']]); 
0

Aunque estoy de acuerdo con las respuestas dadas, en realidad puedes lograr lo que dices con getters y setters. Por ejemplo:

var a = [1]; 
//This makes a["blah"] refer to a[0] 
a.__defineGetter__("blah", function(){return this[0]}); 
//This makes a["blah"] = 5 actually store 5 into a[0] 
a.__defineSetter__("blah", function(val){ this[0] = val}); 

alert(a["blah"]); // emits 1 
a["blah"] = 5; 
alert(a[0]); // emits 5 

¿Es esto lo que estás buscando? Creo que hay una manera más moderna y diferente de hacer getters y setters pero no puedo recordar.

+0

Ah, y si vas a hacer esto en un bucle, es probable que necesites cierres por cierto ... solo digo. – Ryan

4
var myArray = Array(); 
myArray["first"] = "Object1"; 
myArray["second"] = "Object2"; 
myArray["third"] = "Object3"; 

Object.keys(myArray);    // returns ["first", "second", "third"] 
Object.keys(myArray).length;  // returns 3 

si desea que el primer elemento a continuación, se puede usar como tal:

myArray[Object.keys(myArray)[0]]; // returns "Object1" 
0

Vine aquí para querer saber si esto es una mala práctica o no, y en su lugar encontró una gran cantidad de personas apareciendo para no entender la pregunta.

Quería tener una estructura de datos que se ordenó pero podría indexarse ​​por clave, por lo que no requeriría iteración para cada búsqueda.

Los tiempos prácticos esto es bastante simple, pero todavía no he leído nada sobre si es una práctica terrible o no.

var roygbiv = []; 
var colour = { key : "red", hex : "#FF0000" }; 
roygbiv.push(colour); 
roygbiv[colour.key] = colour; 
... 
console.log("Hex colours of the rainbow in order:"); 
for (var i = 0; i < roygbiv.length; i++) { 
    console.log(roygbiv[i].key + " is " + roygbiv[i].hex); 
} 

// input = "red"; 
console.log("Hex code of input colour:"); 
console.log(roygbiv[input].hex); 

Lo importante es no cambiar nunca el valor de array [index] o array [clave] directamente una vez que el objeto está configurado o los valores ya no partido. Si la matriz contiene objetos, puede cambiar las propiedades de esos objetos y podrá acceder a las propiedades modificadas por cualquiera de los métodos.

0

La marea ha cambiado en este caso. Ahora puedes hacer eso ... ¡y MÁS! Usando Harmony Proxies definitivamente podrías resolver este problema de muchas maneras.

Deberá verificar que sus entornos de destino lo admitan con quizás un poco de ayuda del harmony-reflect cuña.

Hay un ejemplo muy bueno en Mozilla Developer Network sobre el uso de un Proxy en find an array item object by it's property, que prácticamente lo resume todo.

aquí está mi versión:

var players = new Proxy(
    [{ 
    name: 'monkey', 
    score: 50 
    }, { 
    name: 'giraffe', 
    score: 100 
    }, { 
    name: 'pelican', 
    score: 150 
    }], { 
    get: function(obj, prop) { 
     if (prop in obj) { 
     // default behavior 
     return obj[prop]; 
     } 
     if (typeof prop == 'string') { 

     if (prop == 'rank') { 
      return obj.sort(function(a, b) { 
      return a.score > b.score ? -1 : 1; 
      }); 
     } 

     if (prop == 'revrank') { 
      return obj.sort(function(a, b) { 
      return a.score < b.score ? -1 : 1; 
      }); 
     } 

     var winner; 
     var score = 0; 
     for (var i = 0; i < obj.length; i++) { 
      var player = obj[i]; 
      if (player.name == prop) { 
      return player; 
      } else if (player.score > score) { 
      score = player.score; 
      winner = player; 
      } 
     } 

     if (prop == 'winner') { 
      return winner; 
     } 
     return; 
     } 

    } 
    }); 

    console.log(players[0]); // { name: 'monkey', score: 50 } 
    console.log(players['monkey']); // { name: 'monkey', score: 50 } 
    console.log(players['zebra']); // undefined 
    console.log(players.rank); // [ { name: 'pelican', score: 150 },{ name: 'giraffe', score: 100 }, { name: 'monkey', score: 50 } ] 
    console.log(players.revrank); // [ { name: 'monkey', score: 50 },{ name: 'giraffe', score: 100 },{ name: 'pelican', score: 150 } ] 
    console.log(players.winner); // { name: 'pelican', score: 150 }