按字符串属性值对对象数组进行排序

4095
我有一个JavaScript对象的数组:
var objs = [ 
    { first_nom: 'Laszlo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];

如何在JavaScript中按last_nom的值对它们进行排序?
我知道sort(a,b),但似乎只适用于字符串和数字。我需要在我的对象中添加toString()方法吗?

2
大小写敏感或不敏感排序? - Peter Mortensen
61个回答

16
使用Intl.Collator对对象进行排序,特别是在您想要进行自然字符串排序的情况下(即["1","2","10","11","111"])。

const files = [
 {name: "1.mp3", size: 123},
 {name: "10.mp3", size: 456},
 {name: "100.mp3", size: 789},
 {name: "11.mp3", size: 123},
 {name: "111.mp3", size: 456},
 {name: "2.mp3", size: 789},
];

const naturalCollator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});

files.sort((a, b) => naturalCollator.compare(a.name, b.name));

console.log(files);

注意:Intl.Collator的undefined构造函数参数表示区域设置,可以是明确的ISO 639-1语言代码,如en,或者当undefined时表示系统默认区域设置。 浏览器对Intl.Collator的支持

1
这是一个不错的方法。 - David Scott Kirby

16

将Ege的动态解决方案与Vinay的想法相结合,您将得到一个很好的稳健解决方案:

Array.prototype.sortBy = function() {
  function _sortByAttr(attr) {
    var sortOrder = 1;
    if (attr[0] == "-") {
      sortOrder = -1;
      attr = attr.substr(1);
    }
    return function(a, b) {
      var result = (a[attr] < b[attr]) ? -1 : (a[attr] > b[attr]) ? 1 : 0;
      return result * sortOrder;
    }
  }

  function _getSortFunc() {
    if (arguments.length == 0) {
      throw "Zero length arguments not allowed for Array.sortBy()";
    }
    var args = arguments;
    return function(a, b) {
      for (var result = 0, i = 0; result == 0 && i < args.length; i++) {
        result = _sortByAttr(args[i])(a, b);
      }
      return result;
    }
  }
  return this.sort(_getSortFunc.apply(null, arguments));
}

Usage:

  // Utility for printing objects
  Array.prototype.print = function(title) {
    console.log("************************************************************************");
    console.log("**** " + title);
    console.log("************************************************************************");
    for (var i = 0; i < this.length; i++) {
      console.log("Name: " + this[i].FirstName, this[i].LastName, "Age: " + this[i].Age);
    }
  }

// Setup sample data
var arrObj = [{
    FirstName: "Zach",
    LastName: "Emergency",
    Age: 35
  },
  {
    FirstName: "Nancy",
    LastName: "Nurse",
    Age: 27
  },
  {
    FirstName: "Ethel",
    LastName: "Emergency",
    Age: 42
  },
  {
    FirstName: "Nina",
    LastName: "Nurse",
    Age: 48
  },
  {
    FirstName: "Anthony",
    LastName: "Emergency",
    Age: 44
  },
  {
    FirstName: "Nina",
    LastName: "Nurse",
    Age: 32
  },
  {
    FirstName: "Ed",
    LastName: "Emergency",
    Age: 28
  },
  {
    FirstName: "Peter",
    LastName: "Physician",
    Age: 58
  },
  {
    FirstName: "Al",
    LastName: "Emergency",
    Age: 51
  },
  {
    FirstName: "Ruth",
    LastName: "Registration",
    Age: 62
  },
  {
    FirstName: "Ed",
    LastName: "Emergency",
    Age: 38
  },
  {
    FirstName: "Tammy",
    LastName: "Triage",
    Age: 29
  },
  {
    FirstName: "Alan",
    LastName: "Emergency",
    Age: 60
  },
  {
    FirstName: "Nina",
    LastName: "Nurse",
    Age: 54
  }
];

//Unit Tests
arrObj.sortBy("LastName").print("LastName Ascending");
arrObj.sortBy("-LastName").print("LastName Descending");
arrObj.sortBy("LastName", "FirstName", "-Age").print("LastName Ascending, FirstName Ascending, Age Descending");
arrObj.sortBy("-FirstName", "Age").print("FirstName Descending, Age Ascending");
arrObj.sortBy("-Age").print("Age Descending");


1
谢谢你的想法!顺便说一下,请不要鼓励人们更改数组原型(请看我的示例末尾的警告)。 - Ege Özcan

15

还有一个选项:

var someArray = [...];

function generateSortFn(prop, reverse) {
    return function (a, b) {
        if (a[prop] < b[prop]) return reverse ? 1 : -1;
        if (a[prop] > b[prop]) return reverse ? -1 : 1;
        return 0;
    };
}

someArray.sort(generateSortFn('name', true));

默认情况下,它按升序排序。


1
如果需要按多个字段排序的略微更改版本,请参见此处:https://dev59.com/s2w05IYBdhLWcg3w9mnd#38037580。 - ravshansbox
看起来这可能是下一个: 导出函数生成排序函数( prop: string, reverse: boolean = false ): (...args: any) => number { 返回 (a, b) => { 返回 a[prop] < b[prop] ? reverse ? 1 : -1 : a[prop] > b[prop] ? reverse ? -1 : 1 : 0; }; } - Den Kerny
同意,但在某些情况下,我没有必要查看实用函数。 - Den Kerny

13

一种简单的方法:

objs.sort(function(a,b) {
  return b.last_nom.toLowerCase() < a.last_nom.toLowerCase();
});

注意,'.toLowerCase()' 是必要的,以防止在比较字符串时出现错误。


5
您可以使用箭头函数,使代码更加简洁: objs.sort((a,b) => b.last_nom.toLowerCase() < a.last_nom.toLowerCase()); - Sertage
这是错误的,原因与此处所解释的相同。 - Patrick Roberts
2
箭头函数不适用于ES5。许多引擎仍然受限于ES5。在我的情况下,我发现上面的答案要好得多,因为我使用的是ES5引擎(由公司强制)。 - dylanh724

13

警告!
不建议使用此解决方案,因为它不能产生排序后的数组。这里保留该想法供将来参考。

objs.sort(function(a,b){return b.last_nom>a.last_nom})

3
实际上它似乎没有起作用,不得不使用被接受的答案。它没有正确排序。 - madprops

13

以下是我的观点:

“order”参数是可选的,默认值为“ASC”,表示升序。

它适用于带有重音的字符,并且不区分大小写。

注意:它对数组进行排序并返回原始的数组。

function sanitizeToSort(str) {
  return str
    .normalize('NFD')                   // Remove accented and diacritics
    .replace(/[\u0300-\u036f]/g, '')    // Remove accented and diacritics
    .toLowerCase()                      // Sort will be case insensitive
  ;
}

function sortByProperty(arr, property, order="ASC") {
  arr.forEach((item) => item.tempProp = sanitizeToSort(item[property]));
  arr.sort((a, b) => order === "ASC" ?
      a.tempProp > b.tempProp ?  1 : a.tempProp < b.tempProp ? -1 : 0
    : a.tempProp > b.tempProp ? -1 : a.tempProp < b.tempProp ?  1 : 0
  );
  arr.forEach((item) => delete item.tempProp);
  return arr;
}

片段

function sanitizeToSort(str) {
  return str
    .normalize('NFD')                   // Remove accented characters
    .replace(/[\u0300-\u036f]/g, '')    // Remove diacritics
    .toLowerCase()
  ;
}

function sortByProperty(arr, property, order="ASC") {
  arr.forEach((item) => item.tempProp = sanitizeToSort(item[property]));
  arr.sort((a, b) => order === "ASC" ?
      a.tempProp > b.tempProp ?  1 : a.tempProp < b.tempProp ? -1 : 0
    : a.tempProp > b.tempProp ? -1 : a.tempProp < b.tempProp ?  1 : 0
  );
  arr.forEach((item) => delete item.tempProp);
  return arr;
}

const rockStars = [
  { name: "Axl",
    lastname: "Rose" },
  { name: "Elthon",
    lastname: "John" },
  { name: "Paul",
    lastname: "McCartney" },
  { name: "Lou",
    lastname: "Reed" },
  { name: "freddie",             // Works on lower/upper case
    lastname: "mercury" },
  { name: "Ámy",                 // Works on accented characters too
    lastname: "winehouse"}

];

sortByProperty(rockStars, "name");

console.log("Ordered by name A-Z:");
rockStars.forEach((item) => console.log(item.name + " " + item.lastname));

sortByProperty(rockStars, "lastname", "DESC");

console.log("\nOrdered by lastname Z-A:");
rockStars.forEach((item) => console.log(item.lastname + ", " + item.name));


如果列表中包含大小写字母组合的名称,则无法正常工作。 - Ankesh Pandey
@AnkeshPandey 感谢您指出这个问题。我已经修复了它。 - cbdeveloper

12

鉴于原始例子:

var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];

按多个字段排序:

objs.sort(function(left, right) {
    var last_nom_order = left.last_nom.localeCompare(right.last_nom);
    var first_nom_order = left.first_nom.localeCompare(right.first_nom);
    return last_nom_order || first_nom_order;
});

注意事项

  • a.localeCompare(b) 是被 普遍支持 的,可返回-1,0,1,分别代表 a<ba==ba>b
  • 最后一行中的 ||last_nom 优先权,而非 first_nom
  • 减法适用于数字字段:var age_order = left.age - right.age;
  • 为了反转顺序进行否定,return -last_nom_order || -first_nom_order || -age_order;

12

一个简单的函数,按照对象的属性对数组进行排序:

function sortArray(array, property, direction) {
    direction = direction || 1;
    array.sort(function compare(a, b) {
        let comparison = 0;
        if (a[property] > b[property]) {
            comparison = 1 * direction;
        } else if (a[property] < b[property]) {
            comparison = -1 * direction;
        }
        return comparison;
    });
    return array; // Chainable
}

使用方法:

var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];

sortArray(objs, "last_nom"); // Asc
sortArray(objs, "last_nom", -1); // Desc

这个解决方案对我来说完美地实现了双向排序。谢谢。 - Coder0997

12

Ege Özcan的代码的附加描述参数:

function dynamicSort(property, desc) {
    if (desc) {
        return function (a, b) {
            return (a[property] > b[property]) ? -1 : (a[property] < b[property]) ? 1 : 0;
        }
    }
    return function (a, b) {
        return (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
    }
}

1
"desc" 是什么意思?是指“降序”吗?还是“描述符”?还是其他什么? - Peter Mortensen

10

使用Ramda:

npm install ramda

import R from 'ramda'
var objs = [ 
    { first_nom: 'Lazslo', last_nom: 'Jamf'     },
    { first_nom: 'Pig',    last_nom: 'Bodine'   },
    { first_nom: 'Pirate', last_nom: 'Prentice' }
];
var ascendingSortedObjs = R.sortBy(R.prop('last_nom'), objs)
var descendingSortedObjs = R.reverse(ascendingSortedObjs)

Ramda是什么?你能否添加一个参考链接(例如,非裸链接)?(但请勿包含“编辑:”,“更新:”或类似内容 - 答案应该看起来像今天写的)。 - Peter Mortensen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接