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

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

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

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

Ответ 1

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

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

Ответ 2

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

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

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

Ответ 3

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

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

Ответ 4

Я согласен с Swingley answer, и я думаю, что это важный момент, когда многие из этих более сложных решений отсутствуют. Если вас интересуют только ключи в ассоциативном массиве, а все значения - "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']. Это может или не имеет значения в вашем коде.

Ответ 5

Нет никакого краткого способа прямого манипулирования "ключами" объекта 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); }
};

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

Ответ 6

Получить ключи в первом цикле 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]);

Ответ 7

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

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

Функция keys в исходном тексте подчеркивания:

// Retrieve the names of an object properties.
// Delegates to **ECMAScript 5** 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);
};

Ответ 8

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

Ответ 9

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

Ответ 10

Мне очень нравится идея прототипа 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);