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


2763

我有一个JavaScript对象数组:

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

如何按last_nomJavaScript中的值对它们进行排序?

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


7
除非您要编写自己的比较函数或排序程序,否则此脚本允许您执行此操作:thomasfrank.se/sorting_things.html
Baversjo,2009年

最快的方法是使用同构排序数组模块,该模块可在浏览器和节点中本机运行,支持任何类型的输入,计算字段和自定义排序顺序。
劳埃德

Answers:


3916

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

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((a,b) => (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0)); 

441
或内联:objs.sort(function(a,b){return(a.last_nom> b.last_nom)?1:(((b.last_nom> a.last_nom)?-1:0);});
Marco Demaio 2010年


176
return a.last_nom.localeCompare(b.last_nom)也可以。
Cerbrus

146
对于那些希望在字段为数字字段中进行排序的人,比较函数正文:return a.value - b.value;(ASC)
Andre Figueiredo 2014年

20
@Cerbrus localeCompare在使用外语重音字符时很重要,而且也更优雅。
Marcos Lima

839

您还可以创建一个动态排序函数,该函数根据传递的对象的值对它们进行排序:

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

子类化数组

对于可以使用ES6(允许扩展本机对象)的幸运者:

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

这将启用以下功能:

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

请注意,JavaScript中的属性名称可以是任何字符串,并且如果您具有以“-”开头的属性(极不可能,并且可能不是一个好主意),则需要修改dynamicSort函数以将其他内容用作反向排序指示符。
EgeÖzcan2013年

我注意到dynamicSort()上面的示例中的将大写字母放在小写字母之前。举例来说,如果我有值APdAklinAbe-在ASC排序结果应该是AbeAklinAPd。但随着你的榜样,结果是APdAbeAklin。无论如何要纠正这种行为?
劳埃德银行

6
@LloydBanks如果您严格使用此字符串,则可以使用var result = a[property].localeCompare(b[property]);代替var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
EgeÖzcan'8

1
@EgeÖzcan应该将项目放在顶部或底部,这是我最终使用pastebin.com/g4dt23jU
dzh 18'Jan

1
这是一个很好的解决方案,但是如果您比较数字就会遇到一个问题。请在检查之前添加以下内容:if( !isNaN(a[property]) ) a[property] = Number(a[property]); if( !isNaN(b[property]) ) b[property] = Number(b[property]);
Ivijan StefanStipić'18

414

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

29
自JS 1.1开始提供此功能,此功能的粗箭头为ES6 / 2015。但是仍然非常有用,而且我认为是最好的答案
乔恩·哈丁

13
@PratikKelwalkar:如果需要反转,只需切换a和b比较:objs.sort((a,b)=> b.last_nom.localeCompare(a.last_nom));
弗拉德·贝兹登(Flad Bezden)'16

是有可能使用一个索引以及以解决字段排序:不是last_nom在阵列中使用刚数:1
and-bri

4
@VladBezden感谢您的回答!该解决方案是第一个花费很少的编程工作并具有正确的排序结果和字符串数组的解决方案,例如:[“ Name1”,“ Name10”,“ Name2”,“ something”,“ Name11”]。我进行了排序,可以与objs.sort((a, b) => a.last_nom.localeCompare(b.last_nom, undefined, {numberic: true}));
Scipper

1
为此,请使数字降序:.sort((a,b)=> b.numberProperty-a.numberProperty)。升序:.sort((a,b)=> a.numberProperty-b.numberProperty)
塞巴斯蒂安·帕滕

188

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

18
大卫,您能编辑答案说,var sortedObjs = _.sortBy( objs, 'first_nom' );objs因此不会对其进行排序。该函数将返回一个排序数组。这将使其更加明确。
2014年

10
进行反向排序:var reverseSortedObjs = _.sortBy( objs, 'first_nom' ).reverse();
Erdal G.

2
您需要加载javascript库“下划线”:<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"> </script>
and-bri

5
Lodash适合那些喜欢的人
WoJ

2
在lodash中,这是相同的:var sortedObjs = _.sortBy( objs, 'first_nom' );或者如果您希望以其他顺序输入它:var sortedObjs = _.orderBy( objs, ['first_nom'],['dsc'] );
Travis Heeter

186

不知道为什么人们如此复杂:

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

交换运算符以按相反的字母顺序对其进行排序。


17
这实际上是不正确的,因为在sort中使用的函数应该返回-1、0或1,但是上面的函数返回一个布尔值。排序在chrome中工作正常,但在PhantomJS中失败。参见code.google.com/p/phantomjs/issues/detail?id=1090
schup 2014年

一些引擎说明了愚蠢,这种方式就是利用了这一点。我已使用适当的版本更新了回复。
p3lim 2014年

19
我建议编辑以取出第一个版本。它更简洁,因此看起来更有吸引力,但是它不起作用,至少不能可靠地起作用。如果有人在一个浏览器中尝试了它并且起作用了,他们甚至可能没有意识到自己有问题(尤其是如果他们没有阅读评论)。第二个版本正常工作,因此实际上不需要第一个版本。
凯特

2
@Simon我真的很感谢首先拥有“略有错误”的版本,因为更严格的实现需要花费几秒钟的时间来解析和理解,如果没有它,将变得更加困难。
亚伦·谢尔曼

1
@ Lion789只需这样做:if(a.count == b.count) return a.name > b.name; else return a.count > b.count;
p3lim

62

如果您的姓氏重复,则可以按名字对它们进行排序-

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?我看到每个人都将这些数字用作返回值,但是为什么呢?谢谢。
克里斯

1
@ Chris22返回负数意味着b应该a在数组之后。如果返回正数,则表示a应该紧随其后b。如果0返回,则表示它们相等。您可以随时阅读文档:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…–
BadFeelingAboutThis

@BadFeelingAboutThis感谢您的解释和链接。信不信由你,1, 0, -1在我问这个问题之前,我使用了Google的各种代码片段。我只是没有找到我需要的信息。
克里斯

48

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

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

更新:不再修改原始阵列。


5
它不只是返回另一个数组。但实际上是对原始的排序!
Vinay Aggarwal

如果要确保您使用的是带有数字的自然排序(即0、1、2、10、11等),请使用带有Radix集的parseInt。developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/…这样:return(parseInt(a [p],10)> parseInt(b [p],10))吗?1:(parseInt(a [p],10)<parseInt(b [p],10))?-1:0;
保罗

@codehuntr感谢您纠正它。但是我想与其创建排序函数来进行这种区分,不如让我们创建一个单独的函数来修复数据类型更好。因为sort函数不能告诉哪个属性将包含哪种数据。:)
Vinay Aggarwal

非常好。反向箭头可获得asc / desc效果。
阿卜杜勒·萨迪克·雅尔辛

4
永远不会提出修改核心对象原型的解决方案
pbanka

39

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

7
在问题中,字符串用于比较而不是数字。您的答案非常适合按数字排序,但不适用于按字符串进行比较。
smcstewart

a.value - b.value用来比较对象的属性(编号在这种情况下),可用于数据的不同时间通过。例如,可以使用正则表达式比较每对相邻的字符串
0leg

1
@ 0leg我想在这里看到一个使用正则表达式以这种方式比较字符串的示例。
鲍勃·斯坦

如果您需要按ID对其进行排序,则此实现非常好。是的,您建议使用正则表达式比较相邻的字符串,这会使解决方案更加复杂,而如果将正则表达式与给定的解决方案一起使用,则此简化版本的目的将是其他目的。简单是最好的。
surendrapanday

33

旧答案不正确:

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

更新

从博尚的评论:

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()将投true1false0


可以,但是由于某种原因结果不稳定
TitanFighter

@ AO17不,不会。您不能减去字符串。
Patrick Roberts

15
应该这样做: arr.sort((a, b) => a.name < b.name ? -1 : (a.name > b.name ? 1 : 0))
让·弗朗索瓦·博尚

3
@Jean-FrançoisBeauchamp,您的解决方案可以完美工作,而且效果更好。
阿山

Number的第3个简单明了!
Kojo

29

除了使用自定义比较功能之外,还可以使用自定义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();

29

Lodash.js(超集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来指定应匹配的属性的路径。我们将根据嵌套的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();

或者什么时候您更喜欢流而不是链

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

28

您可以使用

最简单的方法: Lodash

https://lodash.com/docs/4.17.10#orderBy

此方法类似于_.sortBy,不同之处在于,它允许指定迭代对象的排序顺序。如果未指定订单,则所有值均按升序排序。否则,请为相应值的降序指定“ desc”的顺序,或为升序指定“ asc”的顺序。

争论

collection(Array | Object):要迭代的collection。[iteratees = [_。identity]](Array [] | Function [] | Object [] | string []):进行排序的迭代器。[orders](string []):迭代对象的排序顺序。

退货

(数组):返回新排序的数组。


var _ = require('lodash');
var homes = [
    {"h_id":"3",
     "city":"Dallas",
     "state":"TX",
     "zip":"75201",
     "price":"162500"},
    {"h_id":"4",
     "city":"Bevery Hills",
     "state":"CA",
     "zip":"90210",
     "price":"319250"},
    {"h_id":"6",
     "city":"Dallas",
     "state":"TX",
     "zip":"75000",
     "price":"556699"},
    {"h_id":"5",
     "city":"New York",
     "state":"NY",
     "zip":"00010",
     "price":"962500"}
    ];

_.orderBy(homes, ['city', 'state', 'zip'], ['asc', 'desc', 'asc']);

23

这里有很多好的答案,但是我想指出,可以很简单地扩展它们,以实现更复杂的排序。您唯一要做的就是使用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可以使您走很长一段路,而无需外部库或复杂的代码。这也是非常有效的,因为不必进行字符串解析


23

用法示例:

objs.sort(sortBy('last_nom'));

脚本:

/**
 * @description
 * Returns a function which will sort an
 * array of objects by the given key.
 *
 * @param  {String}  key
 * @param  {Boolean} reverse
 * @return {Function}
 */
const sortBy = (key, reverse) => {

  // Move smaller items towards the front
  // or back of the array depending on if
  // we want to sort the array in reverse
  // order or not.
  const moveSmaller = reverse ? 1 : -1;

  // Move larger items towards the front
  // or back of the array depending on if
  // we want to sort the array in reverse
  // order or not.
  const moveLarger = reverse ? -1 : 1;

  /**
   * @param  {*} a
   * @param  {*} b
   * @return {Number}
   */
  return (a, b) => {
    if (a[key] < b[key]) {
      return moveSmaller;
    }
    if (a[key] > b[key]) {
      return moveLarger;
    }
    return 0;
  };
};

感谢您对此进行分类,我试图理解为什么将数字1, 0, -1用于排序。即使上面有您的解释,看起来也很好-我仍然不太了解。我总是认为-1使用数组长度属性时,即: arr.length = -1表示未找到该项目。我可能在这里混了一些东西,但是您能帮我理解为什么用数字1, 0, -1来确定顺序吗?谢谢。
克里斯

1
这并不完全准确,但是可能会这样想:将传递给array.sort的函数对数组中的每个项目调用一次,作为名为“ a”的参数。每个函数调用的返回值是与下一个项目“ b”相比应如何更改项目“ a”的索引(当前位置编号)。索引指示数组的顺序(0、1、2等),因此,如果“ a”位于索引5处,并且您返回-1,则5 + -1 == 4(将其移到更近的前面)5 + 0 == 5 (将其保留在当前位置)等。每次遍历该数组,比较两个邻居,直到到达末尾,剩下一个排序数组。
杰米·梅森

感谢您抽出宝贵的时间对此做进一步解释。因此,使用您的解释和MDN Array.prototype.sort,我将告诉您我的想法:与a和相比b,如果a大于b将1加到索引中,a然后将其放在后面b,如果a小于b,从中减去1 a并将其放在的前面b。如果ab相同,则将0加到原a处。
克里斯22年

22

我没有看到建议的这种特定方法,所以这是我喜欢使用的一种简洁的比较方法,该方法对string和都适用number

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

const sortBy = fn => (a, b) => {
  const fa = fn(a);
  const fb = fn(b);
  return -(fa < fb) || +(fa > fb);
};

const getLastName = o => o.last_nom;
const sortByLastName = sortBy(getLastName);

objs.sort(sortByLastName);
console.log(objs.map(getLastName));

这是对以下内容的解释sortBy()

sortBy()接受fn从对象中选择要用作比较值的值,并返回可以直接传递给的函数Array.prototype.sort()。在这个例子中,我们使用o.last_nom作为比较值,所以每当我们收到过两个对象Array.prototype.sort(),如

{ first_nom: 'Lazslo', last_nom: 'Jamf' }

{ first_nom: 'Pig', last_nom: 'Bodine' }

我们用

(a, b) => {
  const fa = fn(a);
  const fb = fn(b);
  return -(fa < fb) || +(fa > fb);
};

比较它们。

记住fn = o => o.last_nom,我们可以将比较功能扩展为等效功能

(a, b) => {
  const fa = a.last_nom;
  const fb = b.last_nom;
  return -(fa < fb) || +(fa > fb);
};

逻辑或||运算符具有短路功能,在此非常有用。由于其工作原理,上述函数的主体意味着

if (fa < fb) return -1;
if (fa > fb) return 1;
return 0;

另外,这是ECMAScript 5中没有箭头功能的等效项,不幸的是,它更加冗长:

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

var sortBy = function (fn) {
  return function (a, b) {
    var fa = fn(a);
    var fb = fn(b);
    return -(fa < fb) || +(fa > fb);
  };
};

var getLastName = function (o) { return o.last_nom };
var sortByLastName = sortBy(getLastName);

objs.sort(sortByLastName);
console.log(objs.map(getLastName));


17

我知道这个问题太旧了,但是我没有看到任何类似我的实现。
此版本基于Schwartzian转换惯用法

function sortByAttribute(array, ...attrs) {
  // generate an array of predicate-objects contains
  // property getter, and descending indicator
  let predicates = attrs.map(pred => {
    let descending = pred.charAt(0) === '-' ? -1 : 1;
    pred = pred.replace(/^-/, '');
    return {
      getter: o => o[pred],
      descend: descending
    };
  });
  // schwartzian transform idiom implementation. aka: "decorate-sort-undecorate"
  return array.map(item => {
    return {
      src: item,
      compareValues: predicates.map(predicate => predicate.getter(item))
    };
  })
  .sort((o1, o2) => {
    let i = -1, result = 0;
    while (++i < predicates.length) {
      if (o1.compareValues[i] < o2.compareValues[i]) result = -1;
      if (o1.compareValues[i] > o2.compareValues[i]) result = 1;
      if (result *= predicates[i].descend) break;
    }
    return result;
  })
  .map(item => item.src);
}

这是一个如何使用它的示例:

let games = [
  { name: 'Mashraki',          rating: 4.21 },
  { name: 'Hill Climb Racing', rating: 3.88 },
  { name: 'Angry Birds Space', rating: 3.88 },
  { name: 'Badland',           rating: 4.33 }
];

// sort by one attribute
console.log(sortByAttribute(games, 'name'));
// sort by mupltiple attributes
console.log(sortByAttribute(games, '-rating', 'name'));

14

排序(更多)复杂的对象数组

由于您可能会遇到更复杂的数据结构(例如此数组),因此我将扩展解决方案。

TL; DR

是基于@ege-Özcan非常可爱的答案的可插拔版本。

问题

我遇到了以下内容,无法更改。我也不想暂时弄平物体。我也不想使用下划线/ lodash,主要是出于性能方面的考虑以及自己实现的乐趣。

var People = [
   {Name: {name: "Name", surname: "Surname"}, Middlename: "JJ"},
   {Name: {name: "AAA", surname: "ZZZ"}, Middlename:"Abrams"},
   {Name: {name: "Name", surname: "AAA"}, Middlename: "Wars"}
];

目标

目标是主要按照 People.Name.namePeople.Name.surname

障碍物

现在,在基本解决方案中,使用括号符号来计算要动态排序的属性。不过,在这里,我们还必须动态构建括号符号,因为您可能希望People['Name.name']可以工作-但这没有用。

People['Name']['name']另一方面,简单地做是静态的,仅允许您降低n级。

这里的主要附加功能是沿着对象树移动,并确定您必须指定的最后一个叶子以及任何中间叶子的值。

var People = [
   {Name: {name: "Name", surname: "Surname"}, Middlename: "JJ"},
   {Name: {name: "AAA", surname: "ZZZ"}, Middlename:"Abrams"},
   {Name: {name: "Name", surname: "AAA"}, Middlename: "Wars"}
];

People.sort(dynamicMultiSort(['Name','name'], ['Name', '-surname']));
// Results in...
// [ { Name: { name: 'AAA', surname: 'ZZZ' }, Middlename: 'Abrams' },
//   { Name: { name: 'Name', surname: 'Surname' }, Middlename: 'JJ' },
//   { Name: { name: 'Name', surname: 'AAA' }, Middlename: 'Wars' } ]

// same logic as above, but strong deviation for dynamic properties 
function dynamicSort(properties) {
  var sortOrder = 1;
  // determine sort order by checking sign of last element of array
  if(properties[properties.length - 1][0] === "-") {
    sortOrder = -1;
    // Chop off sign
    properties[properties.length - 1] = properties[properties.length - 1].substr(1);
  }
  return function (a,b) {
    propertyOfA = recurseObjProp(a, properties)
    propertyOfB = recurseObjProp(b, properties)
    var result = (propertyOfA < propertyOfB) ? -1 : (propertyOfA > propertyOfB) ? 1 : 0;
    return result * sortOrder;
  };
}

/**
 * Takes an object and recurses down the tree to a target leaf and returns it value
 * @param  {Object} root - Object to be traversed.
 * @param  {Array} leafs - Array of downwards traversal. To access the value: {parent:{ child: 'value'}} -> ['parent','child']
 * @param  {Number} index - Must not be set, since it is implicit.
 * @return {String|Number}       The property, which is to be compared by sort.
 */
function recurseObjProp(root, leafs, index) {
  index ? index : index = 0
  var upper = root
  // walk down one level
  lower = upper[leafs[index]]
  // Check if last leaf has been hit by having gone one step too far.
  // If so, return result from last step.
  if (!lower) {
    return upper
  }
  // Else: recurse!
  index++
  // HINT: Bug was here, for not explicitly returning function
  // https://stackoverflow.com/a/17528613/3580261
  return recurseObjProp(lower, leafs, index)
}

/**
 * Multi-sort your array by a set of properties
 * @param {...Array} Arrays to access values in the form of: {parent:{ child: 'value'}} -> ['parent','child']
 * @return {Number} Number - number for sort algorithm
 */
function dynamicMultiSort() {
  var args = Array.prototype.slice.call(arguments); // slight deviation to base

  return function (a, b) {
    var i = 0, result = 0, numberOfProperties = args.length;
    // REVIEW: slightly verbose; maybe no way around because of `.sort`-'s nature
    // Consider: `.forEach()`
    while(result === 0 && i < numberOfProperties) {
      result = dynamicSort(args[i])(a, b);
      i++;
    }
    return result;
  }
}

JSBin的工作示例


2
为什么?这不是原始问题的答案,“目标”可以通过People.sort((a,b)=> {return a.Name.name.localeCompare(b.Name.name)|| a.Name .surname.localeCompare(b.Name.surname)})
Tero Tolonen

12

另一种选择:

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
:如果需要稍微改变版本由多个字段排序在这里stackoverflow.com/questions/6913512/...
Ravshan Samandarov

看起来可能是下一个:导出函数generateSortFn(prop:string,reverse:boolean = false):(... args:any)=> number {return(a,b)=> {return a [prop ] <b [prop]吗?逆转 ?1:-1:a [prop]> b [prop]?逆转 ?-1:1:0; }; }
Den Kerny

我希望代码比最短的代码更具可读性。
Ravshan Samandarov

同意,但是在某些情况下,我没有必要查看效用函数。
4:00

11

一个简单的函数,通过属性对对象数组进行排序

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

此解决方案非常适合我进行双向排序。谢谢你
manjuvreddy

10

一种简单的方法:

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

看到'.toLowerCase()'有必要防止在比较字符串时出错。


4
您可以使用箭头功能使代码更优雅:objs.sort( (a,b) => b.last_nom.toLowerCase() < a.last_nom.toLowerCase() );
Sertage

这是错误的,原因与此处说明的原因相同。
Patrick Roberts

2
箭头功能不支持ES5。大量的引擎仍然仅限于ES5。以我
为例,

9

EgeÖzcan代码的其他desc参数

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

9

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

用法:

// 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'13

8

根据您的示例,您需要按两个字段(姓,名)而不是一个字段进行排序。您可以使用Alasql库在一行中进行这种排序:

var res = alasql('SELECT * FROM ? ORDER BY last_nom, first_nom',[objs]);

在jsFiddle尝试此示例。



8

给出原始示例:

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;

7

您可能需要将它们转换为小写形式,以防止混淆。

objs.sort(function (a,b) {

var nameA=a.last_nom.toLowerCase(), nameB=b.last_nom.toLowerCase()

if (nameA < nameB)
  return -1;
if (nameA > nameB)
  return 1;
return 0;  //no sorting

})

7
function compare(propName) {
    return function(a,b) {
        if (a[propName] < b[propName])
            return -1;
        if (a[propName] > b[propName])
            return 1;
        return 0;
    };
}

objs.sort(compare("last_nom"));

1
请考虑编辑您的文章,以添加更多有关代码功能以及为什么它可以解决问题的解释。通常仅包含代码(即使它在起作用)的答案通常不会帮助OP理解他们的问题。
德伦米

7

使用Ramda,

npm安装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)

7

尝试这个,

UPTO ES5

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


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


IN ES6 & above:

// Ascending sort
items.sort((a, b) => a.value - b.value);

// Descending Sort
 items.sort((a, b) => b.value - a.value);

最佳和简便的解决方案
Omar Hasan

6

这是一个简单的问题,不知道为什么人们会有如此复杂的解决方案。
一个简单的排序功能(基于快速排序算法):

function sortObjectsArray(objectsArray, sortKey)
        {
            // Quick Sort:
            var retVal;

            if (1 < objectsArray.length)
            {
                var pivotIndex = Math.floor((objectsArray.length - 1) / 2);  // middle index
                var pivotItem = objectsArray[pivotIndex];                    // value in the middle index
                var less = [], more = [];

                objectsArray.splice(pivotIndex, 1);                          // remove the item in the pivot position
                objectsArray.forEach(function(value, index, array)
                {
                    value[sortKey] <= pivotItem[sortKey] ?                   // compare the 'sortKey' proiperty
                        less.push(value) :
                        more.push(value) ;
                });

                retVal = sortObjectsArray(less, sortKey).concat([pivotItem], sortObjectsArray(more, sortKey));
            }
            else
            {
                retVal = objectsArray;
            }

            return retVal;
        }

使用示例:

var myArr = 
        [
            { val: 'x', idx: 3 },
            { val: 'y', idx: 2 },
            { val: 'z', idx: 5 },
        ];
myArr = sortObjectsArray(myArr, 'idx');

5
如何在js中实现快速排序是一个简单的解决方案?简单的算法,但不是简单的解决方案。
安德鲁

这很简单,因为它不使用任何外部库,并且不更改对象的原型。我认为,代码的长度不会直接影响代码的复杂性
Gil Epshtain 2015年

2
好吧,让我用不同的词来尝试一下:如何重新发明轮子是一个简单的解决方案?
Roberto14 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.