Itérer sur un tableau associatif Javascript dans l'ordre trié


109

Disons que j'ai un tableau associatif Javascript (aka hash, aka dictionary):

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

Comment puis-je parcourir les clés dans un ordre trié? Si cela aide à simplifier les choses, je n'ai même pas besoin des valeurs (elles ne sont que le chiffre 1).


11
pourquoi utilisez-vous la nouvelle construction Array () et ensuite l'utilisez-vous comme un objet?
Luke Schafer

@Luke .. J'ai fait ça au début aussi, venant d'un background PHP. J'ai appris maintenant cependant :)
alex

20
@Luke: parce que je suis inexpérimenté, semble-t-il. Pouvez-vous publier la bonne manière dans une réponse?
mike

4
Vous pouvez simplement créer n'importe quel objet. En Javascript, il n'y a aucune différence entre un dictionnaire / "tableau nommé" et un objet normal. Vous pouvez donc accéder à a ['b'] avec ab et vice versa. Le moyen le plus court de créer un objet est a = {};.
Lodewijk

Réponses:


124

Vous ne pouvez pas les parcourir directement, mais vous pouvez trouver toutes les clés, puis les trier.

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"]

Cependant, il n'est pas nécessaire de faire de la variable «a» un tableau. Vous l'utilisez vraiment comme un objet et vous devriez le créer comme ceci:

var a = {};
a["key"] = "value";

28
Vous devriez toujours vérifier dans la forboucle si obj.hasOwnProperty(key).
viam0Zah

3
@Lalit - si vous faites référence au commentaire de Torok, c'est parce que rien n'interfère avec le prototype de l'objet, sur lequel vous ne pouvez pas compter.
Luke Schafer

1
+1 à Torok. Ce serait bien si la réponse incluait hasOwnProperty ().
Jon Onstott

136

Vous pouvez utiliser la méthode intégrée Object.keys :

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

(Remarque: cela ne fonctionne pas dans les très anciens navigateurs ne prenant pas en charge EcmaScript5, notamment IE6, 7 et 8. Pour des statistiques détaillées à jour, voir ce tableau )


@ michael667 Probablement parce que IE 7 et 8 sont encore largement utilisés (malheureusement, merci MS)
Alexander Reifinger

1
IE7 est à 0,5% et IE8 est à 8% à partir de maintenant, heureusement.
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

J'adore celui-ci, merci. voici mon code exploitant ceci, $ (Object.keys (list)). map (function (i, e) {return n + '=' + list [n];}). get (). join ('&'); // concat for url querystring
Elaine

1
Mise à jour 2016: Cela devrait probablement être la réponse acceptée
rinogo

14

vous pouvez même le prototyper sur un objet:

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] ]);
    }
}

et l'utilisation:

var myObj = { a:1, b:2 };
myObj.iterateSorted(function(value)
{
    alert(value);
} 

3
J'ai voté pour cette réponse, cela me semblait plutôt bien, mais il s'est avéré que cela casse jquery :( stackoverflow.com/questions/1827458/… et en général, c'est considéré comme une très mauvaise idée "Vous ne devriez jamais étendre Object.prototype. Il fait bien plus que break jQuery; il brise complètement la fonctionnalité "object-as-hashtables" de Javascript. Ne le faites pas. Vous pouvez demander à John Resig, et il vous dira la même chose. "
msanjay

Vous savez quoi? Je déteste aussi les prototypes :) Je ne les utilise jamais et décourage activement leur utilisation. J'ai en quelque sorte ressenti cela il y a 3,5 ans lorsque j'ai écrit cette réponse, mais je l'ai quand même suggéré ... merci d'avoir fourni les informations. En passant, il NE DOIT PAS casser les frameworks car ils devraient TOUJOURS utiliser hasOwnProperty lors de l'itération d'objets
Luke Schafer

Voici un exemple utilisant les valeurs au lieu des clés pour le tri tout en conservant la key -> valuerelation.
Xeoncross

6

Je suis d'accord avec la réponse de Swingley , et je pense que c'est un point important que beaucoup de ces solutions plus élaborées manquent. Si vous n'êtes concerné que par les clés du tableau associatif et que toutes les valeurs sont «1», stockez simplement les «clés» en tant que valeurs dans un tableau.

Au lieu de:

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

Utilisation:

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

Le seul inconvénient est que vous ne pouvez pas déterminer si une clé spécifique est définie aussi facilement. Voir cette réponse à la fonction javascript inArray pour une réponse à ce problème. Un problème avec la solution présentée est qu'elle a.hasValue('key')sera légèrement plus lente que a['key']. Cela peut ou non avoir une importance dans votre code.


3

Il n'y a pas de moyen concis de manipuler directement les "clés" d'un objet Javascript. Ce n'est pas vraiment conçu pour ça. Avez-vous la liberté de placer vos données dans quelque chose de mieux qu'un objet normal (ou un tableau, comme le suggère votre exemple de code)?

Si tel est le cas, et si votre question peut être reformulée comme suit: "Quel objet de type dictionnaire dois-je utiliser si je veux parcourir les clés dans un ordre trié?" alors vous pourriez développer un objet comme celui-ci:

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 vous n'avez aucun contrôle sur le fait que les données se trouvent dans un objet normal, cet utilitaire convertira l'objet régulier en votre dictionnaire entièrement fonctionnel:

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

C'était une définition d'objet (au lieu d'une fonction de constructeur réutilisable) pour plus de simplicité; modifier à volonté.


2

Récupérez les clés dans la première forboucle, triez-la, utilisez le résultat trié dans la 2ème forboucle.

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]);

2

Vous pouvez utiliser la keysfonction de la bibliothèque underscore.js pour obtenir les clés, puis la sort()méthode array pour les trier:

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

La keysfonction dans le code source du trait de soulignement:

// 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);
};

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>

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>');
}


0

J'aime beaucoup l'idée de prototype de @ luke-schafer, mais j'entends aussi ce qu'il dit sur les problèmes avec les prototypes. Qu'en est-il d'utiliser une fonction 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);

Cela semble éliminer les problèmes avec les prototypes et fournir toujours un itérateur trié pour les objets. Cependant, je ne suis pas vraiment un gourou de JavaScript, alors j'aimerais savoir si cette solution contient des défauts cachés.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.