【问题标题】:How to sort an associative array by its values in Javascript?如何在 Javascript 中按关联数组的值对关联数组进行排序?
【发布时间】:2011-07-09 03:58:23
【问题描述】:

我有关联数组:

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

按其值排序(降序)的最优雅的方法是什么,结果将是一个数组,其中相应的索引按此顺序排列:

sub2, sub3, sub1, sub4, sub0

【问题讨论】:

  • 由于对象属性没有语言定义的顺序 - 你不能(也许在某些 JS 引擎中,取决于它们实现属性的特定方式)。

标签: javascript arrays sorting associative


【解决方案1】:

Javascript 没有您想象中的“关联数组”。相反,您只需能够使用类似数组的语法(如您的示例中所示)设置对象属性,以及迭代对象属性的能力。

这样做的结果是无法保证您迭代属性的顺序,因此对于它们来说没有什么比排序更好的了。相反,您需要将对象属性转换为“真实”数组(这确实保证了顺序)。这是一个代码 sn-p 用于将对象转换为两个元组(两个元素数组)的数组,按照您的描述对其进行排序,然后对其进行迭代:

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>`
});
&lt;p id='res'&gt;Result:&lt;br/&gt;&lt;br/&gt;&lt;p&gt;

【讨论】:

  • tuples.sort 函数可以清理为 tuples.sort(function(a, b) { return a[1] - b[1]; });
  • @stot — 如果您的值都是数字(如在提问者的示例中),绝对可以。我似乎心不在焉地提供了一个比较函数,它也适用于字符串。 :-)
  • @Ben:是的,你是对的,提供的版本更通用 ;-)
  • @Ben,非常感谢您的发帖。关于String比较函数,是否使用ASCII值比较?
  • @jjwdesign,假设字符串没有被编码,它会按照Unicode码位排序。
【解决方案2】:

我认为这就是你想要的,而不是纠正你对“关联数组”的语义:

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

对于really old browsers,改用这个:

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

您转储一个对象(就像您的对象一样),然后返回一个键数组 - eh 属性 - 按对象的(数字)值降序排序。

仅当您的值是数字时才有效。 Tweek 小function(a,b) 在那里更改排序机制以升序工作,或为string 值工作(例如)。留给读者作为练习。

【讨论】:

  • 我应该注意到现在大多数浏览器只支持 Object.keys() - developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • 为什么 object.keys 更好?
  • @johnktejik 这个答案来自 2012 年 :-) Object.keys() 是当今的标准,而且它更短,可能更快。
  • ... :) 我累了,让我删除这个
【解决方案3】:

How to sort an (associative) array by value? 继续讨论和其他解决方案,最佳解决方案(就我而言)是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;
});

【讨论】:

  • 太棒了!待办事项和对名称进行 alpha 排序:return a.name > b.name.
  • 对于字母排序,比较相同大小写的字符串值,因为sort() 对待它们的方式不同。它把我搞砸了一个小时,直到我找到了这个 stackoverflow.com/questions/6712034/… 示例; a.name.toLowerCase() &gt; b.name.toLowerCase()
【解决方案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;
}

然后你会用一个比较器函数来调用它。

【讨论】:

  • +1 你打败了我。我正在写一个非常相似的解释和一段代码。
【解决方案5】:

在我看来,针对特定情况的最佳方法是 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.keysArray.sort 的一些有用参考

【讨论】:

    【解决方案6】:

    如果您不喜欢元组,这里是 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.
    }
    

    【讨论】:

      【解决方案7】:

      不需要不必要的复杂性...

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

      【讨论】:

      • this的输出是数组的array,不是map
      • var stuff = {"a":1 , "b":3 , "c":0 } sortMapByValue(stuff) [Array[2], Array[2], Array[2]]
      • 我认为这是最干净的。只需在最后添加转换到地图
      【解决方案8】:

      我使用 $.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);
      

      【讨论】:

        【解决方案9】:

        就这样它就在那里,有人正在寻找基于元组的排序。 这将比较数组中对象的第一个元素,而不是第二个元素,依此类推。即在下面的示例中,它将首先按“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 } ]
        

        【讨论】:

          【解决方案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' ]

          【讨论】:

            猜你喜欢
            • 2011-05-01
            • 2013-05-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-02-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多