Ітерація через асоціативний масив Javascript у відсортованому порядку


109

Скажімо, у мене є асоціативний масив Javascript (він же хеш, ака словник):

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

Як я можу перебирати клавіші в упорядкованому порядку? Якщо це допомагає спростити речі, значення мені навіть не потрібні (вони всі просто число 1).


11
чому ви використовуєте нову конструкцію Array (), а потім використовуєте її як об’єкт?
— Люк Шафер

@Luke .. Я це робив і спочатку, виходячи з фону PHP. Я зараз дізнався, хоча :)
— Алекс

20
@Luke: тому що я недосвідчений, здається. Чи можете ви відповісти правильним способом у відповіді?
— Майк

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цикл, якщо obj.hasOwnProperty(key).
— viam0Zah

3
@Lalit - якщо ви маєте на увазі коментар Торока, це тому, що у вас нічого не заважає прототипу об'єкта, на який ви не можете покластися.
— Люк Шафер

1
+1 до Торока. Було б добре, якби відповідь включала hasOwnProperty ().
— Джон Онстотт

136

Ви можете використовувати вбудований метод Object.keys :

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

(Примітка. Це не працює у дуже старих браузерах, які не підтримують EcmaScript5, зокрема IE6, 7 та 8. Детальну інформацію про сучасну статистику див. У цій таблиці )


@ 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 (список)). map (функція (i, e) {return n + '=' + список [n];}). get (). join ('&'); // concat for url querystring
— Elaine

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-об'єкт-хештелі" функцію Javascript. Не робіть цього. Ви можете попросити Джона Резіга, і він скаже вам те саме ".
— msanjay

Знаєш, що? Я теж ненавиджу прототипи :) Я ніколи їх не використовую і активно відштовхую від їх використання. Я почував це так 3,5 років тому, коли я писав цю відповідь, але все одно запропонував її ... дякую за надання інформації. На відміну від цього, НЕ МОГУТЬ порушувати рамки, як вони ВИНАГИ використовують hasOwnProperty під час ітерації об'єктів
— Лука Шафер

Ось приклад використання значень, а не клавіш для сортування , зберігаючи key -> valueзв’язок.
— Xeoncross

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

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>

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, тому я хотів би знати, чи є в цьому рішенні приховані недоліки, які я пропустив.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.