Как отсортировать ассоциативный массив по его значениям в Javascript?

У меня есть ассоциативный массив:

array["sub2"] = 1;
array["sub0"] = -1;
array["sub1"] = 0;
array["sub3"] = 1;
array["sub4"] = 0;

Каков самый элегантный способ сортировки (спуска) по его значениям, где результатом будет массив с соответствующими индексами в этом порядке:

sub2, sub3, sub1, sub4, sub0?

Ответ 1

Javascript не имеет "ассоциативных массивов", как вы думаете о них. Вместо этого у вас просто есть возможность устанавливать свойства объекта, используя синтаксис типа массива (как в вашем примере), а также возможность итерации по свойствам объекта.

Это означает, что нет гарантии относительно порядка, в котором вы перебираете свойства, поэтому для них нет ничего подобного. Вместо этого вам нужно будет преобразовать ваши свойства объекта в "истинный" массив (который гарантирует порядок). Здесь фрагмент кода для преобразования объекта в массив из двух кортежей (двухэлементных массивов), сортируя его по мере описания, затем повторяя его:

var tuples = [];

for (var key in obj) tuples.push([key, obj[key]]);

tuples.sort(function(a, b) {
    a = a[1];
    b = b[1];

    return a < b ? -1 : (a > b ? 1 : 0);
});

for (var i = 0; i < tuples.length; i++) {
    var key = tuples[i][0];
    var value = tuples[i][1];

    // do something with key and value
}

Вы можете обнаружить более естественным обернуть это в функцию, которая выполняет обратный вызов:

function bySortedValue(obj, callback, context) {
  var tuples = [];

  for (var key in obj) tuples.push([key, obj[key]]);

  tuples.sort(function(a, b) {
    return a[1] < b[1] ? 1 : a[1] > b[1] ? -1 : 0
  });

  var length = tuples.length;
  while (length--) callback.call(context, tuples[length][0], tuples[length][1]);
}

bySortedValue({
  foo: 1,
  bar: 7,
  baz: 3
}, function(key, value) {
  document.getElementById('res').innerHTML += `${key}: ${value}<br>`
});
<p id='res'>Result:<br/><br/><p>

Ответ 2

РЕДАКТИРОВАТЬ: люди продолжают голосовать за этот ответ, но он действительно старый. Пожалуйста, пересмотрите, почему вы не просто используете Object.keys() в наше время: var keys = Object.keys(obj);

Вместо того, чтобы исправлять вас в семантике "ассоциативного массива", я думаю, что это то, что вы хотите:

function getSortedKeys(obj) {
    var keys = []; for(var key in obj) keys.push(key);
    return keys.sort(function(a,b){return obj[b]-obj[a]});
}

Вы помещаете объект (как и ваш) в дамп и получаете массив ключей - а это свойства - назад, отсортированные по убыванию (числовому) значению, например, значения объекта, а.

Это работает, только если ваши значения являются числовыми. Настройте небольшую function(a,b) чтобы изменить механизм сортировки для работы по возрастанию или для string значений (например). Оставлено как упражнение для читателя.

Ответ 3

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

Массивы могут иметь только числовые индексы. Вам нужно будет переписать это как объект или массив объектов.

var status = new Array();
status.push({name: 'BOB', val: 10});
status.push({name: 'TOM', val: 3});
status.push({name: 'ROB', val: 22});
status.push({name: 'JON', val: 7});

Если вам нравится метод status.push, его можно отсортировать с помощью:

status.sort(function(a,b) {
    return a.val - b.val;
});

Ответ 4

В JavaScript действительно нет такой вещи, как "ассоциативный массив". То, что у вас есть, - это простой старый объект. Конечно, они работают как ассоциативные массивы, и ключи доступны, но семантики вокруг порядка ключей отсутствуют.

Вы можете превратить свой объект в массив объектов (пары ключ/значение) и отсортировать его:

function sortObj(object, sortFunc) {
  var rv = [];
  for (var k in object) {
    if (object.hasOwnProperty(k)) rv.push({key: k, value:  object[k]});
  }
  rv.sort(function(o1, o2) {
    return sortFunc(o1.key, o2.key);
  });
  return rv;
}

Затем вы вызываете это с помощью функции компаратора.

Ответ 5

Вот вариант ответа ben blank, если вам не нравятся кортежи.

Это сэкономит вам несколько символов.

var keys = [];
for (var key in sortme) {
  keys.push(key);
}

keys.sort(function(k0, k1) {
  var a = sortme[k0];
  var b = sortme[k1];
  return a < b ? -1 : (a > b ? 1 : 0);
});

for (var i = 0; i < keys.length; ++i) {
  var key = keys[i];
  var value = sortme[key];
  // Do something with key and value.
}

Ответ 6

Не требуется ненужное осложнение...

function sortMapByValue(map)
{
    var tupleArray = [];
    for (var key in map) tupleArray.push([key, map[key]]);
    tupleArray.sort(function (a, b) { return a[1] - b[1] });
    return tupleArray;
}

Ответ 7

Я использую $.each jquery, но вы можете сделать это с циклом for, улучшение следующее:

        //.ArraySort(array)
        /* Sort an array
         */
        ArraySort = function(array, sortFunc){
              var tmp = [];
              var aSorted=[];
              var oSorted={};

              for (var k in array) {
                if (array.hasOwnProperty(k)) 
                    tmp.push({key: k, value:  array[k]});
              }

              tmp.sort(function(o1, o2) {
                    return sortFunc(o1.value, o2.value);
              });                     

              if(Object.prototype.toString.call(array) === '[object Array]'){
                  $.each(tmp, function(index, value){
                      aSorted.push(value.value);
                  });
                  return aSorted;                     
              }

              if(Object.prototype.toString.call(array) === '[object Object]'){
                  $.each(tmp, function(index, value){
                      oSorted[value.key]=value.value;
                  });                     
                  return oSorted;
              }               
     };

Итак, теперь вы можете сделать

    console.log("ArraySort");
    var arr1 = [4,3,6,1,2,8,5,9,9];
    var arr2 = {'a':4, 'b':3, 'c':6, 'd':1, 'e':2, 'f':8, 'g':5, 'h':9};
    var arr3 = {a: 'green', b: 'brown', c: 'blue', d: 'red'};
    var result1 = ArraySort(arr1, function(a,b){return a-b});
    var result2 = ArraySort(arr2, function(a,b){return a-b});
    var result3 = ArraySort(arr3, function(a,b){return a>b});
    console.log(result1);
    console.log(result2);       
    console.log(result3);

Ответ 8

Просто так, там, и кто-то ищет кортежи на основе. Это будет сравнивать первый элемент объекта в массиве, чем второй элемент и так далее. то в примере ниже он сначала будет сравнивать "a", затем "b" и т.д.

let arr = [
    {a:1, b:2, c:3},
    {a:3, b:5, c:1},
    {a:2, b:3, c:9},
    {a:2, b:5, c:9},
    {a:2, b:3, c:10}    
]

function getSortedScore(obj) {
    var keys = []; 
    for(var key in obj[0]) keys.push(key);
    return obj.sort(function(a,b){
        for (var i in keys) {
            let k = keys[i];
            if (a[k]-b[k] > 0) return -1;
            else if (a[k]-b[k] < 0) return 1;
            else continue;
        };
    });
}

console.log(getSortedScore(arr))

Выходы

 [ { a: 3, b: 5, c: 1 },
  { a: 2, b: 5, c: 9 },
  { a: 2, b: 3, c: 10 },
  { a: 2, b: 3, c: 9 },
  { a: 1, b: 2, c: 3 } ]

Ответ 9

На мой взгляд, наилучшим подходом для конкретного случая является тот, который предлагается в качестве обычного щука. Небольшое улучшение, которое я хотел бы предложить, работает в современных браузерах:

// aao is the "associative array" you need to "sort"
Object.keys(aao).sort(function(a,b){return aao[b]-aao[a]});

Это может легко применяться и отлично работать в данном конкретном случае, так что вы можете сделать:

let aoo={};
aao["sub2"]=1;
aao["sub0"]=-1;
aao["sub1"]=0;
aao["sub3"]=1;
aao["sub4"]=0;

let sk=Object.keys(aao).sort(function(a,b){return aao[b]-aao[a]});

// now you can loop using the sorted keys in 'sk' to do stuffs
for (let i=sk.length-1;i>=0;--i){
 // do something with sk[i] or aoo[sk[i]]
}

Помимо этого, я предоставляю здесь более "универсальную" функцию, которую вы можете использовать для сортировки даже в более широком диапазоне ситуаций, и в которой сочетаются улучшения, которые я только что предложил, с подходами ответов Бена Бланка (сортировка также строковых значений) и PopeJohnPaulII ( сортировка по определенному объектному полю/свойству) и позволяет вам решить, хотите ли вы использовать восходящий или нисходящий порядок, вот он:

// aao := is the "associative array" you need to "sort"
// comp := is the "field" you want to compare or "" if you have no "fields" and simply need to compare values
// intVal := must be false if you need comparing non-integer values
// desc := set to true will sort keys in descendant order (default sort order is ascendant)
function sortedKeys(aao,comp="",intVal=false,desc=false){
  let keys=Object.keys(aao);
  if (comp!="") {
    if (intVal) {
      if (desc) return keys.sort(function(a,b){return aao[b][comp]-aao[a][comp]});
      else return keys.sort(function(a,b){return aao[a][comp]-aao[a][comp]});
    } else {
      if (desc) return keys.sort(function(a,b){return aao[b][comp]<aao[a][comp]?1:aao[b][comp]>aao[a][comp]?-1:0});
      else return keys.sort(function(a,b){return aao[a][comp]<aao[b][comp]?1:aao[a][comp]>aao[b][comp]?-1:0});
    }
  } else {
    if (intVal) {
      if (desc) return keys.sort(function(a,b){return aao[b]-aao[a]});
      else return keys.sort(function(a,b){return aao[a]-aao[b]});
    } else {
      if (desc) return keys.sort(function(a,b){return aao[b]<aao[a]?1:aao[b]>aao[a]?-1:0});
      else return keys.sort(function(a,b){return aao[a]<aao[b]?1:aao[a]>aao[b]?-1:0});
    }
  }
}

Вы можете проверить функциональность, используя что-то вроде следующего кода:

let items={};
items['Edward']=21;
items['Sharpe']=37;
items['And']=45;
items['The']=-12;
items['Magnetic']=13;
items['Zeros']=37;
//equivalent to:
//let items={"Edward": 21, "Sharpe": 37, "And": 45, "The": -12, ...};

console.log("1: "+sortedKeys(items));
console.log("2: "+sortedKeys(items,"",false,true));
console.log("3: "+sortedKeys(items,"",true,false));
console.log("4: "+sortedKeys(items,"",true,true));
/* OUTPUT
1: And,Sharpe,Zeros,Edward,Magnetic,The
2: The,Magnetic,Edward,Sharpe,Zeros,And
3: The,Magnetic,Edward,Sharpe,Zeros,And
4: And,Sharpe,Zeros,Edward,Magnetic,The
*/

items={};
items['k1']={name:'Edward',value:21};
items['k2']={name:'Sharpe',value:37};
items['k3']={name:'And',value:45};
items['k4']={name:'The',value:-12};
items['k5']={name:'Magnetic',value:13};
items['k6']={name:'Zeros',value:37};

console.log("1: "+sortedKeys(items,"name"));
console.log("2: "+sortedKeys(items,"name",false,true));
/* OUTPUT
1: k6,k4,k2,k5,k1,k3
2: k3,k1,k5,k2,k4,k6
*/

Как я уже сказал, вы можете перебирать отсортированные ключи, если вам нужно делать вещи

let sk=sortedKeys(aoo);
// now you can loop using the sorted keys in 'sk' to do stuffs
for (let i=sk.length-1;i>=0;--i){
 // do something with sk[i] or aoo[sk[i]]
}

Наконец, что не менее важно, некоторые полезные ссылки на Object.keys и Array.sort

Ответ 10

@commonpike ответ "правильный", но, как он продолжает комментировать...

теперь большинство браузеров поддерживают Object.keys()

Да... Object.keys() ПУТЬ лучше.

Но какой еще лучше? Дух, это он в coffeescript!

sortedKeys = (x) -> Object.keys(x).sort (a,b) -> x[a] - x[b]

sortedKeys
  'a' :  1
  'b' :  3
  'c' :  4
  'd' : -1

[ 'd', 'a', 'b', 'c' ]