Iteruj po tablicy asocjacyjnej Javascript w posortowanej kolejności


109

Powiedzmy, że mam tablicę asocjacyjną JavaScript (aka hash, aka słownik):

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

Jak mogę iterować klucze w posortowanej kolejności? Jeśli pomaga to uprościć sprawę, nie potrzebuję nawet wartości (wszystkie są po prostu liczbą 1).


11
dlaczego używasz nowej konstrukcji Array (), a następnie używasz jej jak obiektu?
Luke Schafer

@Luke .. Na początku też to zrobiłem, korzystając z tła PHP. Teraz się jednak nauczyłem :)
alex

20
@Luke: wydaje się, że jestem niedoświadczony. Czy możesz zamieścić poprawną odpowiedź w odpowiedzi?
mike

4
Możesz po prostu stworzyć dowolny obiekt. W Javascript nie ma różnicy między słownikiem / „nazwaną tablicą” a zwykłym obiektem. Możesz zatem uzyskać dostęp do a ['b'] z ab i odwrotnie. Najkrótszym sposobem utworzenia obiektu jest a = {};.
Lodewijk

Odpowiedzi:


124

Nie możesz ich bezpośrednio iterować, ale możesz znaleźć wszystkie klucze, a następnie je po prostu posortować.

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

Jednak nie ma potrzeby, aby zmienna „a” była tablicą. Naprawdę używasz go jako obiektu i powinieneś go stworzyć w następujący sposób:

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

28
Zawsze powinieneś sprawdzić w forpętli, czy obj.hasOwnProperty(key).
viam0Zah

3
@Lalit - jeśli odnosisz się do komentarza Toroka, to dlatego, że nie masz nic, co zakłóca prototyp obiektu, na którym nie możesz polegać.
Luke Schafer

1
+1 do Toroka. Byłoby miło, gdyby odpowiedź zawierała hasOwnProperty ().
Jon Onstott,

136

Możesz użyć wbudowanej metody Object.keys :

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

(Uwaga: to nie działa w bardzo starych przeglądarkach nie obsługujących EcmaScript5, zwłaszcza w IE6, 7 i 8. Szczegółowe aktualne statystyki znajdują się w tej tabeli )


@ michael667 Prawdopodobnie dlatego, że IE 7 i 8 są nadal szeroko używane (niestety, dziękuję MS)
Alexander Reifinger

1
Na szczęście IE7 wynosi 0,5%, a IE8 8%.
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

Uwielbiam ten, dziękuję. oto mój kod wykorzystujący to, $ (Object.keys (list)). map (function (i, e) {return n + '=' + list [n];}). get (). join ('&'); // concat for url querystring
Elaine

1
Aktualizacja 2016: To prawdopodobnie powinna być zaakceptowana odpowiedź
rinogo

14

możesz nawet prototypować go na obiekcie:

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

i zastosowanie:

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

3
Głosowałem za tą odpowiedzią, wydawała się całkiem dobra, ale okazało się, że psuje jquery :( stackoverflow.com/questions/1827458/... i ogólnie jest uważana za bardzo zły pomysł. „Nigdy nie powinieneś rozszerzać Object.prototype. złamać jQuery; całkowicie psuje funkcję „object-as-hashtables” w Javascript. Nie rób tego. Możesz zapytać Johna Resiga, a powie ci to samo. ”
msanjay,

Wiesz co? Prototypów też nienawidzę :) Nigdy ich nie używam i aktywnie odradzam. Czułem się tak 3,5 roku temu, kiedy pisałem tę odpowiedź, ale i tak zasugerowałem ... dzięki za podanie informacji. Nawiasem mówiąc, NIE POWINIEN to łamać frameworków, ponieważ powinny ZAWSZE używać hasOwnProperty podczas iteracji obiektów
Luke Schafer,

Oto przykład wykorzystujący wartości zamiast kluczy do sortowania przy jednoczesnym zachowaniu key -> valuerelacji.
Xeoncross

6

Zgadzam się z odpowiedzią Swingleya i myślę, że jest to ważna kwestia, której brakuje wielu z tych bardziej wyszukanych rozwiązań. Jeśli interesują Cię tylko klucze w tablicy asocjacyjnej, a wszystkie wartości to „1”, po prostu zapisz „klucze” jako wartości w tablicy.

Zamiast:

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

Posługiwać się:

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

Jedyną wadą jest to, że nie można określić, czy określony klucz jest ustawiony tak łatwo. Zobacz odpowiedź na temat funkcji javascript inArray, aby uzyskać odpowiedź na ten problem. Jednym z problemów z przedstawionym rozwiązaniem jest to, że a.hasValue('key')będzie ono nieco wolniejsze niż a['key']. To może, ale nie musi mieć znaczenia w Twoim kodzie.


3

Nie ma zwięzłego sposobu bezpośredniego manipulowania „kluczami” obiektu Javascript. To nie jest do tego przeznaczone. Czy masz swobodę umieszczania danych w czymś lepszym niż zwykły obiekt (lub tablica, jak sugeruje twój przykładowy kod)?

Jeśli tak, i czy pytanie można przeformułować jako „Jakiego obiektu podobnego do słownika powinienem użyć, jeśli chcę iterować po kluczach w kolejności posortowanej?” wtedy możesz stworzyć taki obiekt:

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

Jeśli nie masz kontroli nad faktem, że dane znajdują się w zwykłym obiekcie, to narzędzie przekształci zwykły obiekt w w pełni funkcjonalny słownik:

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

Dla uproszczenia była to definicja obiektu (zamiast funkcji konstruktora wielokrotnego użytku); edytować do woli.


2

Zdobądź klucze w pierwszej forpętli, posortuj je, użyj posortowanego wyniku w drugiej forpętli.

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

Możesz użyć keysfunkcji z biblioteki underscore.js, aby pobrać klucze, a następnie sort()metody tablicowej, aby je posortować:

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

keysFunkcja w kodzie źródłowym podkreślenia jest:

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

Bardzo podoba mi się pomysł na prototyp @ luke-schafera, ale słyszę też, co mówi o problemach z prototypami. A co z użyciem prostej funkcji?

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

Wydaje się, że eliminuje to problemy z prototypami i nadal zapewnia posortowany iterator dla obiektów. Nie jestem jednak guru JavaScript, więc chciałbym wiedzieć, czy to rozwiązanie ma ukryte wady, które przegapiłem.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.