Как отсортировать ассоциативный массив по его значениям в Javascript? - PullRequest
82 голосов
/ 05 марта 2011

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

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

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

sub2, sub3, sub1, sub4, sub0

?

Ответы [ 10 ]

113 голосов
/ 05 марта 2011

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>
77 голосов
/ 05 августа 2012

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

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

для действительно старых браузеров , используйте вместо этого:

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 (например). Оставлено в качестве упражнения для читателя.

15 голосов
/ 09 октября 2012

Продолжение обсуждения и другие решения, рассмотренные на Как отсортировать (ассоциативный) массив по значению? с лучшим решением (для моего случая) по 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;
});
5 голосов
/ 05 марта 2011

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

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

4 голосов
/ 10 января 2015

Никаких ненужных осложнений не требуется ...

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;
}
4 голосов
/ 02 мая 2013

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

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

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.
}
1 голос
/ 30 июня 2018

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

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

1 голос
/ 19 июля 2012

я использую $ .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);
0 голосов
/ 27 июня 2017

Так оно и есть, и кто-то ищет кортежи на основе. Это позволит сравнить первый элемент объекта в массиве, затем второй элемент и так далее. то есть в приведенном ниже примере он будет сначала сравниваться по «а», затем по «б» и т. д.

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

* 1004 Выходы *

 [ { 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 } ]
0 голосов
/ 03 ноября 2015

@ ответ 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' ]

...