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.