it-swarm.com.ru

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

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

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

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

sub2, sub3, sub1, sub4, sub0?
77
John Smith

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

110
Ben Blank

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

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

Правка: люди продолжают голосовать за этот ответ, но он действительно старый. Пожалуйста пересмотреть, почему вы не просто используете Object.keys () в настоящее время: var keys = Object.keys(obj);

74
commonpike

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

На самом деле в 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
Pointy

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

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
bop

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

Это экономит вам несколько персонажей.

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.
}
4
donquixote

я использую $ .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);
1
demosthenes

На мой взгляд, лучшим подходом для конкретного случая является предложенный 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]]
}

Помимо этого, я предоставляю здесь более «общую» функцию, которую вы можете использовать для сортировки даже в более широком диапазоне ситуаций, и в которой сочетаются улучшения, которые я только что предложил, с подходами ответов Ben Blank (сортировка также строковых значений ) и 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

0
danicotra

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

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 } ]
0
Shayan C