Itera su un array associativo Javascript in ordine ordinato


109

Diciamo che ho un array associativo Javascript (aka hash, aka dictionary):

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

Come posso scorrere le chiavi in ​​ordine ordinato? Se aiuta a semplificare le cose, non ho nemmeno bisogno dei valori (sono tutti solo il numero 1).


11
perché stai usando il nuovo costrutto Array () e poi lo usi come un oggetto?
Luke Schafer,

@Luke .. Anch'io l'ho fatto all'inizio, provenendo da uno sfondo PHP. Ora ho imparato però :)
alex

20
@Luke: perché sono inesperto, a quanto pare. Puoi pubblicare una risposta nel modo corretto?
mike

4
Puoi semplicemente creare qualsiasi oggetto. In Javascript non c'è differenza tra un dizionario / "array denominato" e un oggetto normale. È quindi possibile accedere a a ['b'] con ab e viceversa. Il modo più breve per creare un oggetto è a = {};.
Lodewijk

Risposte:


124

Non è possibile iterare direttamente su di esse, ma è possibile trovare tutte le chiavi e quindi ordinarle.

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

Tuttavia non è necessario rendere la variabile "a" un array. Lo stai davvero usando come oggetto e dovresti crearlo in questo modo:

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

28
Dovresti sempre controllare nel forciclo if obj.hasOwnProperty(key).
viam0Zah

3
@Lalit - se ti riferisci al commento di Torok, è perché non hai nulla che interferisca con il prototipo dell'oggetto, su cui non puoi fare affidamento.
Luke Schafer

1
+1 a Torok. Sarebbe bello se la risposta includesse hasOwnProperty ().
Jon Onstott

136

È possibile utilizzare il metodo integrato Object.keys :

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

(Nota: questo non funziona in browser molto vecchi che non supportano EcmaScript5, in particolare IE6, 7 e 8. Per statistiche aggiornate dettagliate, vedere questa tabella )


@ michael667 Probabilmente perché IE 7 e 8 sono ancora ampiamente utilizzati (sfortunatamente, grazie MS)
Alexander Reifinger

1
IE7 è allo 0,5% e IE8 è all'8% al momento, fortunatamente.
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

Adoro questo, grazie. ecco il mio codice che fa leva su questo, $ (Object.keys (list)). map (function (i, e) {return n + '=' + list [n];}). get (). join ('&'); // concat for url querystring
Elaine

1
Aggiornamento 2016: questa dovrebbe probabilmente essere la risposta accettata
rinogo

14

potresti persino prototiparlo su un oggetto:

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

e l'utilizzo:

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

3
Ho votato positivamente questa risposta, sembrava abbastanza buona, ma si è scoperto che rompe jquery :( stackoverflow.com/questions/1827458/… e in generale è considerata una pessima idea "Non dovresti mai estendere Object.prototype. Fa molto di più di break jQuery; rompe completamente la funzione "object-as-hashtables" di Javascript. Non farlo. Puoi chiedere a John Resig, e lui ti dirà la stessa cosa. "
msanjay

Sai cosa? Odio anche i prototipi :) Non li uso mai e ne sconsiglio vivamente l'uso. Mi sono sentito così 3,5 anni fa quando ho scritto questa risposta, ma l'ho suggerito comunque ... grazie per aver fornito le informazioni. Per inciso, NON DOVREBBE rompere i framework poiché dovrebbero SEMPRE usare hasOwnProperty durante l'iterazione di oggetti
Luke Schafer,

Di seguito è riportato un esempio che utilizza i valori anziché le chiavi per l'ordinamento pur mantenendo la key -> valuerelazione.
Xeoncross

6

Sono d'accordo con la risposta di Swingley e penso che sia un punto importante che mancano molte di queste soluzioni più elaborate. Se sei interessato solo alle chiavi dell'array associativo e tutti i valori sono "1", memorizza semplicemente le "chiavi" come valori in un array.

Invece di:

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

Uso:

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

L'unico svantaggio è che non è possibile determinare se una chiave specifica è impostata con la stessa facilità. Vedi questa risposta alla funzione javascript inArray per una risposta a questo problema. Un problema con la soluzione presentata è che a.hasValue('key')sarà leggermente più lenta di a['key']. Questo può o non può avere importanza nel tuo codice.


3

Non esiste un modo conciso per manipolare direttamente le "chiavi" di un oggetto Javascript. Non è davvero progettato per questo. Hai la libertà di inserire i tuoi dati in qualcosa di meglio di un normale oggetto (o un Array, come suggerisce il tuo codice di esempio)?

In tal caso, e se la tua domanda potesse essere riformulata come "Quale oggetto simile a un dizionario dovrei usare se voglio iterare sulle chiavi in ​​ordine ordinato?" allora potresti sviluppare un oggetto come questo:

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

Se non hai il controllo sul fatto che i dati si trovano in un oggetto normale, questa utility convertirà l'oggetto normale nel tuo dizionario completamente funzionale:

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

Questa era una definizione di oggetto (invece di una funzione di costruzione riutilizzabile) per semplicità; modificare a piacimento.


2

Ottieni le chiavi nel primo forciclo, ordinalo, usa il risultato ordinato nel 2 ° 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]);

2

Puoi usare la keysfunzione dalla libreria underscore.js per ottenere le chiavi, quindi il sort()metodo array per ordinarle:

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

La keysfunzione nel codice sorgente del trattino basso:

// 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

Mi piace molto l'idea del prototipo di @ luke-schafer, ma sento anche quello che sta dicendo sui problemi con i prototipi. Che ne dici di usare una semplice funzione?

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

Questo sembra eliminare i problemi con i prototipi e fornire ancora un iteratore ordinato per gli oggetti. Non sono davvero un guru di JavaScript, quindi mi piacerebbe sapere se questa soluzione ha difetti nascosti che mi sono perso.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.