Итерация по ассоциативному массиву Javascript в отсортированном порядке

109

Скажем, у меня есть ассоциативный массив Javascript (он же хеш, он же словарь):

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

Как я могу перебирать ключи в отсортированном порядке? Если это помогает упростить вещи, мне даже не нужны значения (все они просто цифра 1).

Майк
источник
11
почему вы используете новую конструкцию Array (), а затем используете ее как объект?
Люк Шафер
@Luke .. Я тоже сначала сделал это, исходя из фона PHP. А теперь я научился :)
Alex
20
@ Люк: похоже, я неопытен. Можете ли вы правильно опубликовать ответ?
Майк
4
Вы можете просто создать любой объект. В Javascript нет разницы между словарем / «именованным массивом» и обычным объектом. Следовательно, вы можете получить доступ к ['b'] с помощью ab и наоборот. Самый короткий способ создать объект - это a = {};.
Lodewijk

Ответы:

124

Вы не можете перебирать их напрямую, но можете найти все ключи, а затем просто отсортировать их.

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

Однако нет необходимости делать переменную a массивом. На самом деле вы просто используете его как объект и должны создать его следующим образом:

var a = {};
a["key"] = "value";
Мэтью
источник
28
Вы всегда должны проверять forцикл if obj.hasOwnProperty(key).
viam0Zah
3
@Lalit - если вы имеете в виду комментарий Торока, это потому, что у вас нет ничего, что мешает прототипу объекта, на что вы не можете полагаться.
Люк Шафер
1
+1 к Тороку. Было бы неплохо, если бы в ответ был включен hasOwnProperty ().
Джон Онстотт,
136

Вы можете использовать встроенный метод Object.keys :

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

(Примечание: это не работает в очень старых браузерах, не поддерживающих EcmaScript5, особенно в IE6, 7 и 8. Подробную актуальную статистику см. В этой таблице )

Molnarg
источник
@ michael667 Вероятно, потому что IE 7 и 8 все еще широко используются (к сожалению, спасибо, MS)
Александр Рейфингер
1
К счастью, IE7 сейчас составляет 0,5%, а 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
Я люблю это, спасибо. вот мой код, использующий это, $ (Object.keys (list)). map (function (i, e) {return n + '=' + list [n];}). get (). join ('&'); // concat для url-строки запроса
Элейн
1
Обновление 2016: Вероятно, это должен быть принятый ответ
rinogo
14

вы даже можете прототипировать его на объекте:

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

и использование:

var myObj = { a:1, b:2 };
myObj.iterateSorted(function(value)
{
    alert(value);
} 
Люк Шафер
источник
3
Я поддержал этот ответ, он казался неплохим, но оказалось, что он нарушает jquery :( stackoverflow.com/questions/1827458/… и в целом считается очень плохой идеей "Никогда не следует расширять Object.prototype. Он делает гораздо больше, чем break jQuery; он полностью нарушает функцию Javascript «объект как хэш-таблицы». Не делайте этого. Вы можете спросить Джона Ресига, и он скажет вам то же самое »
msanjay
Знаешь что? Я тоже ненавижу прототипы :) Я никогда не использую их и активно не рекомендую их использовать. Я вроде как чувствовал это 3,5 года назад, когда писал этот ответ, но все равно предложил ... спасибо за информацию. В стороне, он НЕ ДОЛЖЕН нарушать фреймворки, поскольку они должны ВСЕГДА использовать hasOwnProperty при итерации объектов,
Люк Шафер,
Вот пример использования значений вместо ключей для сортировки при сохранении key -> valueсвязи.
Xeoncross 01
6

Я согласен с ответом Свингли , и я думаю, что это важный момент, который отсутствует во многих из этих более сложных решений. Если вас интересуют только ключи в ассоциативном массиве и все значения равны «1», просто сохраните «ключи» как значения в массиве.

Вместо того:

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

Использование:

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

Единственным недостатком этого является то, что вы не можете так легко определить, установлен ли конкретный ключ. См. Этот ответ на функцию javascript inArray для ответа на эту проблему. Одна проблема с представленным решением заключается в том, что a.hasValue('key')оно будет немного медленнее, чем a['key']. Это может иметь значение в вашем коде, а может и не иметь значения.

Грант Вагнер
источник
3

Нет краткого способа напрямую управлять «ключами» объекта Javascript. На самом деле он не предназначен для этого. Есть ли у вас свобода поместить данные в нечто лучшее, чем обычный объект (или массив, как предлагает ваш пример кода)?

Если да, и если ваш вопрос можно перефразировать как «Какой словарь-подобный объект мне следует использовать, если я хочу перебирать ключи в отсортированном порядке?» тогда вы можете разработать такой объект:

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

Если у вас нет контроля над тем, что данные находятся в обычном объекте, эта утилита преобразует обычный объект в ваш полнофункциональный словарь:

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

Для простоты это было определение объекта (вместо многократно используемой функции-конструктора); редактировать по желанию.

Трэвис Уилсон
источник
2

Получите ключи в первом forцикле, отсортируйте его, используйте отсортированный результат во втором forцикле.

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
источник
2

Вы можете использовать keysфункцию из библиотеки underscore.js для получения ключей, а затем sort()метод массива для их сортировки:

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

keysФункции в исходном коде Underscore в:

// 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>
Fran Corpier
источник
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

Мне очень нравится идея прототипа @ luke-schafer, но я также слышу, что он говорит о проблемах с прототипами. А как насчет использования простой функции?

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

Похоже, что это устраняет проблемы с прототипами и по-прежнему предоставляет отсортированный итератор для объектов. Однако я не совсем гуру JavaScript, поэтому хотел бы знать, есть ли в этом решении скрытые недостатки, которые я пропустил.

EFC
источник