【问题标题】:Sorting an Array of Objects by two Properties按两个属性对对象数组进行排序
【发布时间】:2026-02-16 10:25:01
【问题描述】:

我有一个对象数组,我想按两个属性排序:

  1. 提醒时间戳
  2. 修改时间戳

排序顺序:desc

按一个属性对这个对象进行排序不是问题,但在这种情况下,我不知道如何让它工作。

【问题讨论】:

  • 比较第一个属性。如果两个元素的属性值相同,则仅比较第二个属性。 (你有没有整理过一副扑克牌?)
  • 这些属性的类型是什么?数字、字符串还是日期?

标签: javascript sorting


【解决方案1】:

假设时间戳本身排序正常(例如 ISO8601 和同一时区),请尝试:

myArray.sort(function(a,b) {
  var x = a.RemindingTimestamp - b.RemindingTimestamp;
  return x == 0? a.ModificationTimestamp - b.ModificationTimestamp : x;
}

编辑 - 对 cme​​ts 的响应

通过改变减法的顺序或将结果乘以 -1 来实现降序排序。由于不减去而未排序的日期(例如 2012-04-12)可以通过先转换为日期来处理,例如

// Convert ISO8601 date string to date object
// Assuming date is ISO8601 long format, ignores timezone
function toDate(s) {
  var bits = s.split(/[-T :]/);
  var d = new Date(bits[0], bits[1]-1, bits[2]);
  d.setHours(bits[3], bits[4], parseFloat(bits[5])); 
  return d;
}

// Source data, should end up sorted per n
var myArray = [ 
  {RemindingTimestamp: '2012-04-15T23:15:12Z', 
   ModificationTimestamp: '2012-04-15T23:15:12Z', n: 4},
  {RemindingTimestamp: '2012-04-12T23:15:12Z', 
   ModificationTimestamp: '2012-04-12T23:15:12Z', n: 1},
  {RemindingTimestamp: '2012-04-12T23:15:12Z', 
   ModificationTimestamp: '2012-04-13T23:15:12Z', n: 2},
  {RemindingTimestamp: '2012-04-12T23:15:12Z', 
   ModificationTimestamp: '2012-04-13T23:15:14Z', n: 3}
];

// Sort it
myArray.sort(function(a,b) {
  var x = toDate(a.RemindingTimestamp) - toDate(b.RemindingTimestamp);
  return x? x : toDate(a.ModificationTimestamp) - toDate(b.ModificationTimestamp);
});

// Just to show the result
function sa(o) {
  var result = [], t;
  for (var i=0; i<o.length; i++) {
    t = o[i]; 
      result.push(t.n);
  }
  alert(result);
}

sa(myArray); // 1,2,3,4

如果需要,可以扩展日期字符串到日期对象的转换以处理时区(仅对于符合 ISO8601 的字符串,那些使用时区缩写而不是实际偏移量的字符串是不可靠的)。

【讨论】:

  • @Phrogz — 通过将a - b 更改为b - a 来实现降序排序。 ISO8601短日期排序(如20120412),长日期在减法前可以转换为日期对象。
  • 我喜欢这个解决方案,希望我能做的不仅仅是 +1 :)
【解决方案2】:
function compareObject(obj1, obj2){
    if(obj1.RemindingTimestamp > obj2.RemindingTimestamp)
        return - 1;
    if(obj2.RemindingTimestamp > obj1.RemindingTimestamp)
        return 1;

    // obj1.RemindingTimestamp == obj2.RemindingTimestamp

    if(obj1.ModificationTimestamp > obj2.ModificationTimestamp)
        return -1;
    if(obj2.ModificationTimestamp > obj1.ModificationTimestamp)
        return 1;

    return 0;
}

myObjects.sort(compareObject);

JSFiddle Demo

资源:

【讨论】:

    【解决方案3】:

    自定义比较器采用以下形式:

    myArray.sort(function(a,b){
      var m1=a1.RemindingTimestamp,
          m2=a2.RemindingTimestamp,
          n1=a1.ModificationTimestamp,
          n2=a2.ModificationTimestamp;
      return m1<m2 ? -1 : m1>m2 ? 1 :
             n1<n2 ? -1 : n1>n2 ? 1 : 0;
    });
    

    对于降序排序,交换&lt;&gt;(或交换1-1)。

    虽然您可以在每次需要时创建自己的自定义比较器,但我已经创建了一个明确设计的方法,可以使用 Schwartzian transform 轻松按多个标准进行排序(在某些情况下可能会更快但更需要内存): http://phrogz.net/js/Array.prototype.sortBy.js

    简而言之:

    myArray.sortBy(function(obj){
      return [obj.RemindingTimestamp, obj.ModificationTimestamp];
    }).reverse();
    

    reverse 在那里,因为您提到您想要降序排序。如果RemindingTimestampModificationTimestamp 都是数字,您也可以这样做:

    myArray.sortBy(function(obj){
      return [-obj.RemindingTimestamp, -obj.ModificationTimestamp];
    });
    

    以下是将sortBy 添加到数组的代码:

    (function(){
      // Extend Arrays in a safe, non-enumerable way
      if (typeof Object.defineProperty === 'function'){
        // Guard against IE8's broken defineProperty
        try{Object.defineProperty(Array.prototype,'sortBy',{value:sb}); }catch(e){}
      }
      // Fall back to an enumerable implementation
      if (!Array.prototype.sortBy) Array.prototype.sortBy = sb;
    
      function sb(f){
        for (var i=this.length;i;){
          var o = this[--i];
          this[i] = [].concat(f.call(o,o,i),o);
        }
        this.sort(function(a,b){
          for (var i=0,len=a.length;i<len;++i){
            if (a[i]!=b[i]) return a[i]<b[i]?-1:1;
          }
          return 0;
        });
        for (var i=this.length;i;){
          this[--i]=this[i][this[i].length-1];
        }
        return this;
      }
    })();
    

    以下是文档中的更多示例:

    var a=[ {c:"GK",age:37}, {c:"ZK",age:13}, {c:"TK",age:14}, {c:"AK",age:13} ];
    
    a.sortBy( function(){ return this.age } );                                  
      --> [ {c:"ZK",age:13}, {c:"AK",age:13}, {c:"TK",age:14}, {c:"GK",age:37} ]
    
    a.sortBy( function(){ return [this.age,this.c] } );                         
      --> [ {c:"AK",age:13}, {c:"ZK",age:13}, {c:"TK",age:14}, {c:"GK",age:37} ]
    
    a.sortBy( function(){ return -this.age } );                                 
      --> [ {c:"GK",age:37}, {c:"TK",age:14}, {c:"ZK",age:13}, {c:"AK",age:13} ]
    
    
    var n=[ 1, 99, 15, "2", "100", 3, 34, "foo", "bar" ];
    
    n.sort();
      --> [ 1, "100", 15, "2", 3, 34, 99, "bar", "foo" ]
    
    n.sortBy( function(){ return this*1 } );
      --> [ "foo", "bar", 1, "2", 3, 15, 34, 99, "100" ]
    
    n.sortBy( function(o){ return [typeof o,this] } );
      --> [1, 3, 15, 34, 99, "100", "2", "bar", "foo"]
    
    n.sortBy(function(o){ return [typeof o, typeof o=="string" ? o.length : o] })
      --> [1, 3, 15, 34, 99, "2", "100", "bar", "foo"]
    

    请注意,在最后一个示例中,(typeof this) 恰好与 (typeof o);有关详细信息,请参阅this post

    【讨论】:

      【解决方案4】:

      假设两个属性的可排序格式相同,这是ES6中的另一种深度排序方式:

      const comparingFunction = (a, b) => {
        if (a.property1 < b.property1) {
          return -1;
        }
        if (a.property1 > b.property1) {
          return 1;
        }
      
        if (a.property1 == b.property1) {
          if (a.property2 < b.property2) {
            return -1;
          }
          if (a.property2 > b.property2) {
            return 1;
          }
          return 0;
        }
      };
      
      myArrayOfObjects.sort(comparingFunction);
      

      希望它对某人有所帮助。

      【讨论】:

        【解决方案5】:

        另一个way

        function sortBy(ar) {
          return ar.sort((a, b) => a.RemindingTimestamp  === b.RemindingTimestamp  ?
              a.ModificationTimestamp.toString().localeCompare(b.ModificationTimestamp) :
              a.RemindingTimestamp.toString().localeCompare(b.RemindingTimestamp));
        }
        

        【讨论】: