题 在JavaScript中按字符串属性值对对象数组进行排序


我有一个JavaScript对象数组:

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

我怎样才能按它的值对它们进行排序 last_nom 在JavaScript?

我知道 sort(a,b),但这似乎只适用于字符串和数字。我需要在对象中添加toString方法吗?


1829
2017-07-15 03:17


起源


除非您想编写自己的比较函数或分类器,否则此脚本允许您执行此操作: thomasfrank.se/sorting_things.html - Baversjo


答案:


编写自己的比较函数很容易:

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

或内联(c / o Marco Demaio):

objs.sort(function(a,b) {return (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0);} ); 

2716
2017-07-15 03:35



或者内联:objs.sort(function(a,b){return(a.last_nom> b.last_nom)?1:((b.last_nom> a.last_nom)? - 1:0);}); - Marco Demaio
官方文件: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/... - mikemaccana
return a.last_nom.localeCompare(b.last_nom) 也会奏效。 - Cerbrus
对于那些寻找字段为数字的排序的人,比较函数体: return a.value - b.value; (ASC) - Andre Figueiredo
@Cerbrus localeCompare 在外语中使用重音字符时很重要,也更优雅。 - Marcos Lima


您还可以创建一个动态排序函数,按照您传递的值对对象进行排序:

function dynamicSort(property) {
    var sortOrder = 1;
    if(property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1);
    }
    return function (a,b) {
        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"));

将它添加到原型

(下面的实施灵感来自于 迈克R.回答

我不建议更改本机对象原型,只是举一个例子,以便您可以在自己的对象上实现它 (对于支持它的环境,您也可以使用 Object.defineProperty 如下一节所示,至少没有可枚举的负面副作用,如最后一部分所述)

原型实现将类似于以下(这是一个有效的例子):

//Don't just copy-paste this code. You will break the "for-in" loops
!function() {
    function _dynamicSortMultiple(attr) {
       /* dynamicSortMultiple function body comes here */
    }
    function _dynamicSort(property) {
        /* dynamicSort function body comes here */
    }
    Array.prototype.sortBy = function() {
        return this.sort(_dynamicSortMultiple.apply(null, arguments));
    }
}();

将其添加到原型的“OK”方式

如果您的目标是IE v9.0,那么就像我之前提到的那样,使用 Object.defineProperty 喜欢这个 (工作实例):

//Won't work below IE9, but totally safe otherwise
!function() {
    function _dynamicSortMultiple(attr) {
       /* dynamicSortMultiple function body comes here */
    }
    function _dynamicSort(property) {
        /* dynamicSort function body comes here */
    }
    Object.defineProperty(Array.prototype, "sortBy", {
        enumerable: false,
        writable: true,
        value: function() {
            return this.sort(_dynamicSortMultiple.apply(null, arguments));
        }
    });
}();

这可能是一个可接受的妥协,直到 绑定运算符 到达。

所有这些原型乐趣都可以实现:

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

你应该读这个

如果你使用直接原型访问方法(Object.defineProperty很好),其他代码不检查 hasOwnProperty,小猫死了!好吧,说实话,任何小猫都没有受到伤害,但可能事情会破裂,团队中的其他开发人员都会讨厌你:

evil

看到最后一个“SortBy”?是啊。不酷。尽可能使用Object.defineProperty,否则单独保留Array.prototype。


650
2018-01-21 15:03



请注意,JavaScript中的属性名称可以是任何字符串,如果您的属性以“ - ”开头(极不可能和 大概 不是一个好主意),你需要修改dynamicSort函数以使用其他东西作为反向排序指示器。 - Ege Özcan
我注意到了 dynamicSort() 在上面的示例中,将大写字母放在小写字母之前。例如,如果我有值 APd, Aklin,和 Abe  - 应该是ASC排序的结果 Abe, Aklin, APd。但是以你的例子为例,结果是 APd, Abe, Aklin。无论如何要纠正这种行为? - Lloyd Banks
@LloydBanks如果你严格使用这个字符串,那么你可以使用 var result = a[property].localeCompare(b[property]); 代替 var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;。 - Ege Özcan
就像一个最佳答案,这个在存在undefine属性的情况下失败: [ { a: 5 }, {a:2}, {}, {a:1} ] - dzh
@EgeÖzcan它应该把项目放在顶部或底部,这是我最终使用的实现 pastebin.com/g4dt23jU - dzh


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' );

155
2018-05-10 21:24



大卫,你能否编辑答案, var sortedObjs = _.sortBy( objs, 'first_nom' );。 objs 将 不 因此而被归类。功能会 返回 一个排序的数组。这会使它更明确。 - Jess
反向排序: var reverseSortedObjs = _.sortBy( objs, 'first_nom' ).reverse(); - Erdal G.
你需要加载javascript库“下划线”: <script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"> </script> - and-bri
也有 Lodash 对于喜欢那个的人 - WoJ


不要理解为什么人们会这么复杂:

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

对于更严格的引擎:

objs.sort(function(a, b){
  return a.last_nom == b.last_nom ? 0 : +(a.last_nom > b.last_nom) || -1;
});

交换运算符以按反向字母顺序排序。


141
2018-01-24 19:35



这实际上是不正确的,因为sort中使用的函数应返回-1,0或1,但上面的函数返回一个布尔值。排序在chrome中工作正常,但在PhantomJS中失败。看到 code.google.com/p/phantomjs/issues/detail?id=1090 - schup
有些引擎会造成愚蠢,这种方式就是利用它。我用适当的版本更新了我的回复。 - p3lim
我建议编辑以取出第一个版本。它更简洁,所以看起来更有吸引力,但它不起作用,至少不可靠。如果有人在一个浏览器中尝试它并且它可以工作,他们可能甚至没有意识到他们有问题(特别是如果他们没有阅读评论)。第二个版本正常工作,所以第一个版本确实没有必要。 - Kate
是否有第一个具有多个参数的版本 - 特别是按字母排序,然后按字母排序 - Lion789
@Simon我真的很感激首先使用“略有错误”的版本,因为更严格的实现需要几秒钟来解析和理解,没有它会更难。 - Aaron Sherman


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

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

138
2018-01-29 19:44



这是自JS 1.1以来一直可用的,这个胖箭头是ES6 / 2015的一块。但在我看来仍然非常有用,也是最好的答案 - Jon Harding
@PratikKelwalkar:如果你需要反向它只需切换a和b比较:objs.sort((a,b)=> b.last_nom.localeCompare(a.last_nom)); - Vlad Bezden
是否可以使用索引来解决字段的排序:而不是 last_nom 只使用数组中的数字: 1 ? - and-bri
@VladBezden感谢您的回答!这个解决方案是第一个具有非常小的编程工作和正确的排序结果和一个字符串数组,如:[“Name1”,“Name10”,“Name2”,“其他东西”,“Name11”]。我排序正常工作 objs.sort((a, b) => a.last_nom.localeCompare(b.last_nom, undefined, {numberic: true})); - scipper


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

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

51
2017-07-15 04:03



那 a< b  a >b 格式很有趣。 - Dodekeract


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

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"}]

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


38
2017-07-10 11:54



它不仅仅是返回另一个数组。但实际上对原来的一个进行了分类! - Vinay Aggarwal
如果你想确保使用带数字的自然排序(即0,1,2,10,11等...),请使用parseInt和Radix集。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... so:return(parseInt(a [p],10)> parseInt(b [p],10))? 1:(parseInt(a [p],10)<parseInt(b [p],10))? -1:0; - Paul
@codehuntr感谢您的纠正。但我想,如果我们制作一个单独的函数来修复数据类型,而不是使用sort函数来进行这种敏感化。因为sort函数不能分辨哪个属性将包含哪种数据。 :) - Vinay Aggarwal
非常好。反转箭头以获得asc / desc效果。 - Abdul Sadik Yalcin


您也可以使用自定义创建对象类型,而不是使用自定义比较功能 toString() 方法(由默认比较函数调用):

function Person(firstName, lastName) {
    this.firtName = firstName;
    this.lastName = lastName;
}

Person.prototype.toString = function() {
    return this.lastName + ', ' + this.firstName;
}

var persons = [ new Person('Lazslo', 'Jamf'), ...]
persons.sort();

24
2017-07-15 07:21





这里有很多好的答案,但我想指出它们可以非常简单地扩展以实现更复杂的排序。您唯一需要做的就是使用OR运算符来链接比较函数,如下所示:

objs.sort((a,b)=> fn1(a,b) || fn2(a,b) || fn3(a,b) )

哪里 fn1fn2,...是返回[-1,0,1]的排序函数。这导致“按fn1排序”,“按fn2排序”,这几乎等于SQL中的ORDER BY。

此解决方案基于。的行为 || 运算符,评估为 首先计算可以转换为true的表达式

最简单的形式 只有一个内联函数,如下所示:

// ORDER BY last_nom
objs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) )

有两个步骤 last_nomfirst_nom 排序顺序如下:

// ORDER_BY last_nom, first_nom
objs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) || 
                  a.first_nom.localeCompare(b.first_nom)  )

一般的比较函数 可能是这样的:

// ORDER BY <n>
let cmp = (a,b,n)=>a[n].localeCompare(b[n])

此函数可以扩展为支持数字字段,大小写敏感,任意数据类型等。

您可以通过排序优先级链接它们来使用它:

// ORDER_BY last_nom, first_nom
objs.sort((a,b)=> cmp(a,b, "last_nom") || cmp(a,b, "first_nom") )
// ORDER_BY last_nom, first_nom DESC
objs.sort((a,b)=> cmp(a,b, "last_nom") || -cmp(a,b, "first_nom") )
// ORDER_BY last_nom DESC, first_nom DESC
objs.sort((a,b)=> -cmp(a,b, "last_nom") || -cmp(a,b, "first_nom") )

这里的要点是具有功能方法的纯JavaScript可以在没有外部库或复杂代码的情况下走很长的路。它也非常有效,因为不需要进行字符串解析


14
2018-05-05 11:36