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

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个回答

5651

你可以很容易地编写自己的比较函数:

function compare( a, b ) {
  if ( a.last_nom < b.last_nom ){
    return -1;
  }
  if ( a.last_nom > b.last_nom ){
    return 1;
  }
  return 0;
}

objs.sort( compare );

或者使用内联方式(由Marco Demaio提供):

objs.sort((a,b) => (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0))

如果要对数字进行简化(来自Andre Figueiredo的建议):

objs.sort((a,b) => a.last_nom - b.last_nom); // b - a for reverse sort

558
在行内排序:objs.sort(function(a,b) {return (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0);} );(该代码用于JavaScript中的数组排序,按照对象属性last_nom进行升序排列) - Marco Demaio
45
官方文档链接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort - mikemaccana
299
"return a.last_nom.localeCompare(b.last_nom)" 也可以工作。 - Cerbrus
196
针对那些寻找数值排序的人,比较函数的实现代码为:return a.value - b.value;(升序)。 - Andre Figueiredo
2
你可以使用 charCodeAt 将字符串转换为数字,然后使用上面的数值内联来实现更简洁的一行代码:objs.sort((a,b) => a.last_nom.charCodeAt(0) - b.last_nom.charCodeAt(0));。这避免了丑陋的嵌套三元运算符。 - tbatch
显示剩余4条评论

1093

您也可以创建一个动态排序函数,通过传递的值对对象进行排序:

function dynamicSort(property) {
    var sortOrder = 1;
    if(property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1);
    }
    return function (a,b) {
        /* next line works with strings and numbers, 
         * and you may want to customize it to your needs
         */
        var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
        return result * sortOrder;
    }
}

所以你可以拥有这样的对象数组:

var People = [
    {Name: "Name", Surname: "Surname"},
    {Name:"AAA", Surname:"ZZZ"},
    {Name: "Name", Surname: "AAA"}
];

当你执行以下操作时,它将起作用:

People.sort(dynamicSort("Name"));
People.sort(dynamicSort("Surname"));
People.sort(dynamicSort("-Surname"));

实际上,这已经回答了问题。下面的部分是由于许多人联系我,抱怨它无法处理多个参数而写的。

多个参数

您可以使用以下函数生成具有多个排序参数的排序函数。

function dynamicSortMultiple() {
    /*
     * save the arguments object as it will be overwritten
     * note that arguments object is an array-like object
     * consisting of the names of the properties to sort by
     */
    var props = arguments;
    return function (obj1, obj2) {
        var i = 0, result = 0, numberOfProperties = props.length;
        /* try getting a different result from 0 (equal)
         * as long as we have extra properties to compare
         */
        while(result === 0 && i < numberOfProperties) {
            result = dynamicSort(props[i])(obj1, obj2);
            i++;
        }
        return result;
    }
}

这将使您能够做出类似于以下的操作:

People.sort(dynamicSortMultiple("Name", "-Surname"));

子类化Array

对于那些可以使用ES6的幸运儿来说,可以扩展原生对象:

class MyArray extends Array {
    sortBy(...args) {
        return this.sort(dynamicSortMultiple(...args));
    }
}

那将实现这样的功能:

MyArray.from(People).sortBy("Name", "-Surname");

10
好的。现在这个答案已经有了一个TypeScript版本:https://dev59.com/z8Hqa4cB1Zd3GeqPxVW7#68279093。保持(类型)安全! - Inigo
你永远不应该扩展Array。 - zero_cool
4
@zero_cool 这里并没有扩展数组(原型保持不变),而是从中进行扩展。确实不应该更改本机对象的原型,但正如我所说,这里并非如此。 - Ege Özcan
不测试空值 - serge
@serge 任何字符串和null的比较都会返回false,将null值放在末尾。如果你将a[property] < b[property]改为a[property].localeCompare(b[property]),你可以使用a[property]?.localeCompare(b[property]) ?? 1(如果a的属性为空,则将b作为第一个,如果b在属性上为null,则localeCompare将返回-1 -- 当两个都为null时不合逻辑,因此可能需要进行检查)。 - Ege Özcan

1027

ES6/ES2015或更高版本中,您可以这样做:

objs.sort((a, b) => a.last_nom.localeCompare(b.last_nom));

在ES6/ES2015之前

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

如果值是数字,您不需要使用 localeCompare。您可以使用标准的 > 运算符 - 就像 @muasif80 在答案中提到的那样 - https://dev59.com/UXNA5IYBdhLWcg3wAI3d#67992215 - Gangula

244

Underscore.js

使用 Underscore.js,它体积小而且很棒...

sortBy_.sortBy(list, iterator, [context]) 返回通过将每个值传入迭代器运行的结果进行排序的列表副本。可以使用迭代器作为要排序的属性的字符串名称(例如长度)。

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

var sortedObjs = _.sortBy(objs, 'first_nom');

23
David,请您编辑回答,将其改为“var sortedObjs = _.sortBy( objs, 'first_nom' );”。这样写更加明确:函数将会返回一个已排序的数组,而不会objs本身进行排序。 - Jess
13
要进行倒序排序:var reverseSortedObjs = _.sortBy(objs, 'first_nom').reverse(); - Erdal G.
2
你需要加载 JavaScript 库 "underscore": <script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script> - and-bri
7
也可在 Lodash 中使用(https://lodash.com/docs/4.17.5#sortBy),适合那些更喜欢该库的人。 - WoJ
7
在lodash中,这个代码可以写成:var sortedObjs = _.sortBy( objs, 'first_nom' ); 如果你想按不同的顺序排序,可以写成:var sortedObjs = _.orderBy( objs, ['first_nom'],['dsc'] ); - Travis Heeter
@TravisHeeter 对于 lodash,降序应该是 desc。 - Lys

142
区分大小写
arr.sort((a, b) => a.name > b.name ? 1 : -1);

不区分大小写

arr.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1);

有用提示

如果顺序没有改变(对于相同的字符串),那么条件>将失败并返回-1。但是,如果字符串相同,则返回1-1将导致正确的输出。

另一个选项是使用>=运算符而不是>


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


// Define a couple of sorting callback functions, one with hardcoded sort key and the other with an argument sort key
const sorter1 = (a, b) => a.last_nom.toLowerCase() > b.last_nom.toLowerCase() ? 1 : -1;
const sorter2 = (sortBy) => (a, b) => a[sortBy].toLowerCase() > b[sortBy].toLowerCase() ? 1 : -1;

objs.sort(sorter1);
console.log("Using sorter1 - Hardcoded sort property last_name", objs);

objs.sort(sorter2('first_nom'));
console.log("Using sorter2 - passed param sortBy='first_nom'", objs);

objs.sort(sorter2('last_nom'));
console.log("Using sorter2 - passed param sortBy='last_nom'", objs);


大小写敏感的方法是一个很好的简写方式 - 特别是如果值是数字或日期。 - Gangula
提示:如果您想反转顺序,只需交换“-1”和“1”,例如:从“1:-1”到“-1:1”。 - Gangula
改成(b, a)怎么样 :) - muasif80
2
是的,那也可以。我只是觉得交换 1-1 更加直接和合乎逻辑。 - Gangula
@Gangula:相比参数名称,反向顺序更容易理解,但我想这需要更多的阅读代码的经验(在维护时)。我最喜欢的是省略另一个测试来保持项目不变(返回0),如果项目相同,我不介意交换。 - Herbert Van-Vliet
3
返回相等物品的非零值违反了compareFn合同。该问题在sort()的当前实现中可能不会显现,但它并不具有未来的可靠性和/或可能影响性能。 - user3125367

89

如果您有重复的姓氏,您可以按名字排序。

obj.sort(function(a,b){
  if(a.last_nom< b.last_nom) return -1;
  if(a.last_nom >b.last_nom) return 1;
  if(a.first_nom< b.first_nom) return -1;
  if(a.first_nom >b.first_nom) return 1;
  return 0;
});

@BadFeelingAboutThis 返回-1或1代表什么意思?我理解-1字面上意思是A小于B,但为什么要使用1或-1?我看到每个人都在使用这些数字作为返回值,但为什么?谢谢。 - Chris22
1
@Chris22 如果返回一个负数,意味着在数组中b应该在a之后。如果返回一个正数,则意味着a应该在b之后。如果返回0,则表示它们被视为相等。您可以随时阅读文档:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort - BadFeelingAboutThis
1
@BadFeelingAboutThis 谢谢你的解释和链接。信不信由你,我在这里问之前已经用 1、0、-1 搜索了各种代码片段。只是没有找到我需要的信息。 - Chris22

72

截至2018年,有一个更短、更优雅的解决方案。只需使用 Array.prototype.sort()

示例:

var items = [
  { name: 'Edward', value: 21 },
  { name: 'Sharpe', value: 37 },
  { name: 'And', value: 45 },
  { name: 'The', value: -12 },
  { name: 'Magnetic', value: 13 },
  { name: 'Zeros', value: 37 }
];

// sort by value
items.sort(function (a, b) {
  return a.value - b.value;
});

21
问题中使用了字符串进行比较,而不是数字。你的答案适用于按数字排序,但对于按字符串进行比较并不太好。 - smcstewart
用于比较对象属性(在本例中为数字)的 a.value - b.value 可以应用于各种类型的数据。例如,可以使用正则表达式来比较相邻字符串对。 - 0leg
如果您需要按ID进行排序,那么此实现非常好。是的,您建议使用正则表达式比较相邻字符串,这会使解决方案更加复杂,而如果在给定解决方案中同时使用正则表达式,则其简化版本的目的将是相反的。简单是最好的选择。 - surendrapanday

70

使用原型继承的简单快速解决此问题的方法:

Array.prototype.sortBy = function(p) {
  return this.slice(0).sort(function(a,b) {
    return (a[p] > b[p]) ? 1 : (a[p] < b[p]) ? -1 : 0;
  });
}

示例/用法

objs = [{age:44,name:'vinay'},{age:24,name:'deepak'},{age:74,name:'suresh'}];

objs.sortBy('age');
// Returns
// [{"age":24,"name":"deepak"},{"age":44,"name":"vinay"},{"age":74,"name":"suresh"}]

objs.sortBy('name');
// Returns
// [{"age":24,"name":"deepak"},{"age":74,"name":"suresh"},{"age":44,"name":"vinay"}]

更新: 不再修改原始数组。


8
它不仅仅返回另一个数组,而是实际上对原始数组进行了排序! - Vinay Aggarwal
1
如果您想确保使用自然排序与数字(即0、1、2、10、11等),请使用设置了基数的parseInt。https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt 所以:返回(parseInt(a [p],10)> parseInt(b [p],10))?1:(parseInt(a [p],10)< parseInt(b [p],10))?-1:0; - Paul
@codehuntr 感谢您的纠正。但我想,与其让排序函数执行这种敏感操作,我们最好编写一个单独的函数来修复数据类型。因为排序函数无法确定哪个属性将包含什么类型的数据。 :) - Vinay Aggarwal
1
我认为这只适用于某些属性类型...你需要添加日期/字符串处理等等...例如,如果类型是字符串,则使用return a.localCompare(b)等等。 - Sonic Soul
我认为.slice(0)的目的是创建一个数组的浅拷贝。 - emallove

66

不正确的旧答案:

arr.sort((a, b) => a.name > b.name)

更新

来自Beauchamp的评论

arr.sort((a, b) => a.name < b.name ? -1 : (a.name > b.name ? 1 : 0))

更易读的格式:

arr.sort((a, b) => {
  if (a.name < b.name) return -1
  return a.name > b.name ? 1 : 0
})

不使用嵌套的三元运算符:

arr.sort((a, b) => a.name < b.name ? - 1 : Number(a.name > b.name))

解释: Number() 方法会将 true 转换成 1,将 false 转换成 0


1
它能够工作,但由于某些原因结果不稳定。 - TitanFighter
1
@AO17 不会的。你不能减去字符串。 - Patrick Roberts
16
这个应该可以:arr.sort((a, b) => a.name < b.name ? -1 : (a.name > b.name ? 1 : 0)) ,意思是按照 name 属性进行排序。 - Jean-François Beauchamp
3
@Jean-FrançoisBeauchamp,您的解决方案完美地运行得很好,而且比其他解决方案更好。 - Muhammad Ahsan
第三个带数字的很直接和不错! - Kojo
2
为什么 arr.sort((a, b) => a.name > b.name ? 1 : -1) 不能正常工作?对于字符串,我已经测试过这个方法非常好用。如果你想要不区分大小写的话,可以使用 a.name.toLowerCase()b.name.toLowerCase() - muasif80

45

Lodash是一个包含Underscore.js的超集。

不必为每个简单的逻辑添加框架,但依赖于经过良好测试的实用程序框架可以加速开发并减少错误数量。

Lodash生成非常干净的代码,并促进更多的函数式编程风格。一眼望去,就能明确代码的意图。

OP的问题可以简单地解决如下:

const sortedObjs = _.sortBy(objs, 'last_nom');

需要更多信息吗?例如,我们有以下嵌套对象:

const users = [
  { 'user': {'name':'fred', 'age'48}},
  { 'user': {'name':'barney''age'36 }},
  { 'user': {'name':'wilma'}},
  { 'user': {'name':'betty', 'age'32}}
];

现在我们可以使用_.property 简写user.age 来指定应匹配的属性路径。我们将按嵌套的年龄属性对用户对象进行排序。是的,它允许嵌套属性匹配!

const sortedObjs = _.sortBy(users, ['user.age']);

希望反转它?没问题。使用_.reverse

const sortedObjs = _.reverse(_.sortBy(users, ['user.age']));

希望您想要使用 链式调用 来进行合并吗?

const { chain } = require('lodash');
const sortedObjs = chain(users).sortBy('user.age').reverse().value();

你何时更喜欢使用flow而不是chain?

const { flow, reverse, sortBy } = require('lodash/fp');
const sortedObjs = flow([sortBy('user.age'), reverse])(users);

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