Iterar sobre una matriz asociativa de Javascript en orden ordenado

109

Digamos que tengo una matriz asociativa de Javascript (también conocido como hash, también conocido como diccionario):

var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;

¿Cómo puedo iterar sobre las claves en orden ordenado? Si ayuda a simplificar las cosas, ni siquiera necesito los valores (todos son solo el número 1).

Miguel
fuente
11
¿Por qué estás usando la nueva construcción Array () y luego la usas como un objeto?
Luke Schafer
@ Luke ... También hice esto al principio, viniendo de un fondo PHP. Aunque ahora aprendí :)
alex
20
@Luke: porque parece que no tengo experiencia. ¿Puedes publicar la forma correcta en una respuesta?
Mike
4
Simplemente puede crear cualquier objeto. En Javascript no hay diferencia entre un diccionario / "matriz con nombre" y un objeto normal. Por lo tanto, puede acceder a ['b'] con ab y viceversa. La forma más corta de crear un objeto es a = {};.
Lodewijk

Respuestas:

124

No puede iterar sobre ellos directamente, pero puede encontrar todas las claves y luego ordenarlas.

var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;    

function keys(obj)
{
    var keys = [];

    for(var key in obj)
    {
        if(obj.hasOwnProperty(key))
        {
            keys.push(key);
        }
    }

    return keys;
}

keys(a).sort(); // ["a", "b", "z"]

Sin embargo, no es necesario convertir la variable 'a' en una matriz. Realmente lo estás usando como un objeto y deberías crearlo así:

var a = {};
a["key"] = "value";
Mateo
fuente
28
Siempre debe verificar en el forbucle si obj.hasOwnProperty(key).
viam0Zah
3
@Lalit: si te refieres al comentario de Torok, es porque no tienes nada que interfiera con el prototipo del objeto, en el que no puedes confiar.
Luke Schafer
1
+1 a Torok. Sería bueno si la respuesta incluyera hasOwnProperty ().
Jon Onstott
136

Puede utilizar el método integrado Object.keys :

var sorted_keys = Object.keys(a).sort()

(Nota: esto no funciona en navegadores muy antiguos que no son compatibles con EcmaScript5, en particular IE6, 7 y 8. Para obtener estadísticas detalladas y actualizadas, consulte esta tabla )

Molnarg
fuente
@ michael667 Probablemente porque IE 7 y 8 todavía se usan ampliamente (desafortunadamente, gracias MS)
Alexander Reifinger
1
IE7 está en 0.5% e IE8 en 8% a partir de ahora, afortunadamente.
molnarg
3
if (!Object.keys) { Object.keys = function (obj) { var op, result = []; for (op in obj) { if (obj.hasOwnProperty(op) { result.push(op) } } return result }
Jordan Reiter
Amo este, gracias. aquí está mi código aprovechando esto, $ (Object.keys (lista)). map (function (i, e) {return n + '=' + list [n];}). get (). join ('&'); // concat para url querystring
Elaine
1
Actualización de 2016: probablemente esta debería ser la respuesta aceptada
rinogo
14

incluso podrías crear un prototipo en un objeto:

Object.prototype.iterateSorted = function(worker)
{
    var keys = [];
    for (var key in this)
    {
        if (this.hasOwnProperty(key))
            keys.push(key);
    }
    keys.sort();

    for (var i = 0; i < keys.length; i++)
    {
        worker(this[ keys[i] ]);
    }
}

y el uso:

var myObj = { a:1, b:2 };
myObj.iterateSorted(function(value)
{
    alert(value);
} 
Luke Schafer
fuente
3
Elegí esta respuesta, parecía bastante buena, pero resultó que rompe jquery :( stackoverflow.com/questions/1827458/… y, en general, se considera una muy mala idea "Nunca debes extender Object.prototype. Hace mucho más que rompe jQuery; rompe completamente la función "object-as-hashtables" de Javascript. No lo hagas. Puedes preguntarle a John Resig y él te dirá lo mismo ".
msanjay
¿Sabes que? Yo también odio los prototipos :) Nunca los uso y desaliento activamente su uso. En cierto modo me sentí así hace 3.5 años cuando escribí esta respuesta, pero lo sugerí de todos modos ... gracias por proporcionar la información. Además, NO DEBE romper los marcos, ya que SIEMPRE deben usar hasOwnProperty al iterar objetos
Luke Schafer
Aquí hay un ejemplo que usa los valores en lugar de las claves para ordenar mientras se mantiene la key -> valuerelación.
Xeoncross
6

Estoy de acuerdo con la respuesta de Swingley , y creo que es un punto importante que faltan muchas de estas soluciones más elaboradas. Si solo le preocupan las claves en la matriz asociativa y todos los valores son '1', simplemente almacene las 'claves' como valores en una matriz.

En vez de:

var a = { b:1, z:1, a:1 };
// relatively elaborate code to retrieve the keys and sort them

Utilizar:

var a = [ 'b', 'z', 'a' ];
alert(a.sort());

El único inconveniente de esto es que no puede determinar si una clave específica se configura con la misma facilidad. Vea esta respuesta a la función javascript inArray para obtener una respuesta a ese problema. Un problema con la solución presentada es que a.hasValue('key')va a ser un poco más lenta que a['key']. Eso puede o no importar en su código.

Grant Wagner
fuente
3

No hay una forma concisa de manipular directamente las "claves" de un objeto Javascript. Realmente no está diseñado para eso. ¿Tiene la libertad de poner sus datos en algo mejor que un objeto normal (o una matriz, como sugiere su código de muestra)?

Si es así, y si su pregunta podría reformularse como "¿Qué objeto similar a un diccionario debería usar si quiero iterar sobre las claves en orden ordenado?" entonces podrías desarrollar un objeto como este:

var a = {
  keys : new Array(),
  hash : new Object(),
  set : function(key, value) {
    if (typeof(this.hash[key]) == "undefined") { this.keys.push(key); }
    this.hash[key] = value;
  },
  get : function(key) {
    return this.hash[key];
  },
  getSortedKeys : function() {
    this.keys.sort();
    return this.keys;
  }
};

// sample use
a.set('b',1);
a.set('z',1);
a.set('a',1);
var sortedKeys = a.getSortedKeys();
for (var i in sortedKeys) { print(sortedKeys[i]); }

Si no tiene control sobre el hecho de que los datos están en un objeto regular, esta utilidad convertiría el objeto regular en su diccionario completamente funcional:

a.importObject = function(object) {
  for (var i in object) { this.set(i, object); }
};

Esta fue una definición de objeto (en lugar de una función de constructor reutilizable) por simplicidad; editar a voluntad.

Travis Wilson
fuente
2

Obtenga las claves en el primer forciclo, ordénelas, use el resultado ordenado en el segundo forciclo.

var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;

var b = [];
for (k in a) b.push(k);
b.sort();
for (var i = 0; i < b.length; ++i) alert(b[i]);
pts
fuente
2

Puede usar la keysfunción de la biblioteca underscore.js para obtener las claves, luego el sort()método de matriz para ordenarlas:

var sortedKeys = _.keys(dict).sort();

La keysfunción en el código fuente del guión bajo:

// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = nativeKeys || function(obj) {
    if (obj !== Object(obj)) throw new TypeError('Invalid object');
    var keys = [];
    for (var key in obj) if (_.has(obj, key)) keys.push(key);
    return keys;
};    

// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_.has = function(obj, key) {
    return hasOwnProperty.call(obj, key);
};
Eugene Yarmash
fuente
0
<script type="text/javascript">
    var a = {
        b:1,
        z:1,
        a:1
    }; // your JS Object
    var keys = [];
    for (key in a) {
        keys.push(key);
    }
    keys.sort();
    var i = 0;
    var keyslen = keys.length;
    var str = '';
    //SORTED KEY ITERATION
    while (i < keyslen) {
        str += keys[i] + '=>' + a[keys[i]] + '\n';
        ++i;
    }
    alert(str);
    /*RESULT:
    a=>1
    b=>1
    z=>1
    */
</script>
Fran Corpier
fuente
0

var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;


var keys=Object.keys(a).sort();
for(var i=0,key=keys[0];i<keys.length;key=keys[++i]){
  document.write(key+' : '+a[key]+'<br>');
}

Vlad V
fuente
0

Me gusta mucho la idea del prototipo de @ luke-schafer, pero también escucho lo que está diciendo sobre los problemas con los prototipos. ¿Qué hay de usar una función simple?

function sortKeysAndDo( obj, worker ) {
  var keys = Object.keys(obj);
  keys.sort();
  for (var i = 0; i < keys.length; i++) {
     worker(keys[i], obj[keys[i]]);
  }
}

function show( key, value ) {
  document.write( key + ' : ' + value +'<br>' );
}

var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;

sortKeysAndDo( a, show);

var my_object = { 'c': 3, 'a': 1, 'b': 2 };

sortKeysAndDo( my_object, show);

Esto parece eliminar los problemas con los prototipos y aún proporcionar un iterador ordenado para los objetos. Sin embargo, no soy un gurú de JavaScript, así que me encantaría saber si esta solución tiene fallas ocultas que me perdí.

EFC
fuente