ChatGPT解决这个技术问题 Extra ChatGPT

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

我有一组 JavaScript 对象:

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

如何在 JavaScript 中按 last_nom 的值对它们进行排序?

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

此脚本允许您这样做,除非您想编写自己的比较函数或排序器:thomasfrank.se/sorting_things.html
最快的方法是使用同构 sort-array 模块,它可以在浏览器和节点中本地工作,支持任何类型的输入、计算字段和自定义排序顺序。
函数比较(a,b){如果(a.last_nom < b.last_nom){返回-1; } if ( a.last_nom > b.last_nom ){ 返回 1; } 返回 0; } objs.sort(比较);
objs.sort((a,b) => (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0))
@RobertTalada 这是我的答案stackoverflow.com/a/67021585/7012450

d
dogmatic69

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

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

或简化为数字(c/o Andre Figueiredo):

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

或内联: objs.sort(function(a,b) {return (a.last_nom > b.last_nom) ? 1 : ((b.last_nom > a.last_nom) ? -1 : 0);} );
return a.last_nom.localeCompare(b.last_nom) 也可以。
对于那些寻找字段为数字的排序的人,比较函数体:return a.value - b.value;(ASC)
您可以使用 charCodeAt 将字符串转换为数字,然后使用上面的数字内联以获得更简洁的一行:objs.sort((a,b) => a.last_nom.charCodeAt(0) - b.last_nom.charCodeAt(0));。这避免了丑陋的嵌套三元组。
E
Ege Özcan

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

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

其实这已经回答了这个问题。以下部分是因为很多人联系我,抱怨it doesn't work with multiple parameters而写的。

多个参数

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

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(...args));
    }
}

这将实现这一点:

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

好的。现在有这个答案的 Typescript 版本:stackoverflow.com/a/68279093/8910547。保持(类型)安全! 😉
你永远不应该扩展 Array.
@zero_cool 数组没有在这里扩展(原型保持不变),它是从扩展而来的。您确实不应该更改本机对象的原型,但正如我所说,这不是这里发生的事情。
u
user229044

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

这自 JS 1.1 起就可用,与此相关的胖箭头部分是 ES6/2015 部分。但仍然非常有用,我认为最好的答案
@PratikKelwalkar:如果您需要反转它只需切换 a 和 b 比较: objs.sort((a, b) => b.last_nom.localeCompare(a.last_nom));
如果值是数字,则不需要 localeCompare。您可以使用标准的 > 运算符 - 就像@muasif80 的答案中提到的那样 - stackoverflow.com/a/67992215/6908282
目前这个问题的更简单的解决方案。
恕我直言,这应该是公认的答案
B
Bill Sambrone

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

大卫,您能否将答案编辑为 var sortedObjs = _.sortBy( objs, 'first_nom' );objs不会因此而自行排序。该函数将返回一个排序数组。这将使它更加明确。
反向排序:var reverseSortedObjs = _.sortBy( objs, 'first_nom' ).reverse();
您需要加载 javascript 库“下划线”:<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"> </script>
也可以在 Lodash 中为喜欢那个的人提供
在 lodash 中这将是相同的:var sortedObjs = _.sortBy( objs, 'first_nom' ); 或者如果您希望它以不同的顺序:var sortedObjs = _.orderBy( objs, ['first_nom'],['dsc'] );
m
muasif80

区分大小写

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: '海盗', last_nom: 'Prentice' } ]; // 定义几个排序回调函数,一个带有硬编码排序键,另一个带有参数排序键 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("使用 sorter1 - 硬编码排序属性 last_name", objs); objs.sort(sorter2('first_nom')); console.log("使用 sorter2 - 传递参数 sortBy='first_nom'", objs); objs.sort(sorter2('last_nom')); console.log("使用 sorter2 - 传递参数 sortBy='last_nom'", objs);


区分大小写的方法是一种很好的速记方式——尤其是当值是数字或日期时。
提示:如果您想颠倒顺序,您可以简单地将 -11 交换为例如:从 1 : -1-1 : 1
改变 (a, b) to (b, a) 怎么样 :)
是的,这也有效。我只是发现交换 1 & -1 更直接和合乎逻辑。
@Gangula:与参数名称相反的顺序也更容易获取,但我想无论如何都会有更多的代码阅读经验(在维护的情况下)。我最喜欢的是省略另一个测试以使项目保持不变(返回 0)以防它们相同......如果项目相同,我不介意交换。
B
BadFeelingAboutThis

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

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 返回的负数意味着 b 应该在数组中的 a 之后。如果返回正数,则表示 a 应该在 b 之后。如果返回 0,则表示它们被视为相等。您可以随时阅读文档:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
@BadFeelingAboutThis 感谢您的解释和链接。信不信由你,在我在这里问这个问题之前,我使用 1, 0, -1 搜索了各种代码片段。我只是没有找到我需要的信息。
0
0leg

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

在问题中,字符串用于比较而不是数字。您的答案非常适合按数字排序,但不适用于按字符串进行比较。
用于比较对象属性(本例中为 numbers)的 a.value - b.value 可用于不同时间的数据。例如,正则表达式可用于比较每对相邻的字符串
如果您需要按 ID 对其进行排序,则此实现非常好。是的,您建议使用正则表达式来比较相邻的字符串,这会使解决方案更加复杂,而如果将正则表达式与给定的解决方案一起使用,则此简化版本的目的将是其他情况。简单是最好的。
W
Web_Designer

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

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

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


它不只是返回另一个数组。但实际上对原始的进行排序!
如果您想确保使用数字的自然排序(即 0、1、2、10、11 等...),请使用带有 Radix 集的 parseInt。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 所以:返回 (parseInt(a[p],10) > parseInt(b[p],10)) ? 1 : (parseInt(a[p],10) < parseInt(b[p],10)) ? -1:0;
@codehuntr 感谢您的纠正。但我想与其制作排序函数来进行这种敏感化,不如制作一个单独的函数来修复数据类型。因为排序函数不能不知道哪个属性会包含什么样的数据。 :)
我认为这仅适用于某些道具类型..您想添加日期/字符串处理等..即如果类型是字符串,则使用 return a.localCompare(b) 等..等等。
D
Damian Pavlica

不正确的旧答案:

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


它有效,但由于某种原因结果不稳定
@AO17 不,不会。你不能减去字符串。
应该这样做:arr.sort((a, b) => a.name < b.name ? -1 : (a.name > b.name ? 1 : 0))
@Jean-FrançoisBeauchamp,您的解决方案效果很好,而且效果更好。
为什么 arr.sort((a, b) => a.name > b.name ? 1 : -1 不起作用?对于我已经测试过的字符串,这很好用。如果您希望不区分大小写,请使用 a.name.toLowerCase()b.name.toLowerCase()
N
Nico Van Belle

Lodash.jsUnderscore.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']));

想要使用 chain 将两者结合起来吗?

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

或者你什么时候更喜欢 flow 而不是链

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

t
turivishal

您可以使用最简单的方法:Lodash

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

此方法与 _.sortBy 类似,只是它允许指定要排序的迭代对象的排序顺序。如果未指定 orders,则所有值都按升序排序。否则,为相应值的降序指定“desc”或“asc”为升序排序。

论据

集合(数组|对象):要迭代的集合。 [iteratees=[_.identity]] (Array[]|Function[]|Object[]|string[]): 要排序的迭代对象。 [orders] (string[]):迭代的排序顺序。

退货

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

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

P
Patrick Roberts

我还没有看到建议的这种特殊方法,所以这是我喜欢使用的一种简洁的比较方法,它适用于 stringnumber 类型:

const objs = [ { first_nom: 'Lazslo', last_nom: 'Jamf' }, { first_nom: 'Pig', last_nom: 'Bodine' }, { first_nom: '海盗', last_nom: 'Prentice' } ]; const sortBy = fn => { const cmp = (a, b) => -(a < b) || +(a > b);返回 (a, b) => cmp(fn(a), fn(b)); };常量 getLastName = o => o.last_nom;常量 sortByLastName = sortBy(getLastName); objs.sort(sortByLastName); console.log(objs.map(getLastName));

sortBy() 的解释

sortBy() 接受一个 fn,它从对象中选择一个值以用于比较,并返回一个可以传递给 Array.prototype.sort() 的函数。在本例中,我们比较 o.last_nom。每当我们收到两个对象时,例如

a = { first_nom: 'Lazslo', last_nom: 'Jamf' }
b = { first_nom: 'Pig', last_nom: 'Bodine' }

我们将它们与 (a, b) => cmp(fn(a), fn(b)) 进行比较。鉴于

fn = o => o.last_nom

我们可以将比较函数扩展为 (a, b) => cmp(a.last_nom, b.last_nom)。由于 logical OR (||) 在 JavaScript 中的工作方式,cmp(a.last_nom, b.last_nom) 相当于

if (a.last_nom < b.last_nom) return -1;
if (a.last_nom > b.last_nom) return 1;
return 0;

顺便说一下,这在其他语言中称为 three-way comparison "spaceship" (<=>) operator

最后,这是不使用箭头函数的 ES5 兼容语法:

var objs = [ { first_nom: 'Lazslo', last_nom: 'Jamf' }, { first_nom: 'Pig', last_nom: 'Bodine' }, { first_nom: '海盗', last_nom: 'Prentice' } ];函数 sortBy(fn) { 函数 cmp(a, b) { 返回 -(a < b) || +(a > b); } 返回函数 (a, b) { 返回 cmp(fn(a), fn(b)); }; } function getLastName(o) { return o.last_nom; } var sortByLastName = sortBy(getLastName); objs.sort(sortByLastName); console.log(objs.map(getLastName));


我喜欢这种方法,但我认为在这里使用 -(fa < fb) || +(fa > fb) 的简写是错误的。那就是将多条语句压缩成一行代码。使用 if 语句编写的替代方案将更具可读性,同时仍然相当简洁。我认为为了美观而牺牲可读性是错误的。
@MSOACC 感谢您的意见,但我非常不同意。其他语言实现了执行相同比较的 three-way comparison operator,因此在概念上将其视为 fa <=> fb
嘿帕特里克,我喜欢你的回答,但它只适用于英文字符 (const cmp = (a, b) => -(a < b) || +(a > b);) 想想 ["ä", "a", "c", "b"].sort(cmp) => ["a", "b", "c", "ä"],其中 ä 被推到最后。相反,您可能应该将比较函数更新为: const cmp = (a, b) => a.localeCompare(b); => ["a", "ä", "b", "c"] 干杯并感谢您的回答 ;-)
@rjanjic 感谢您的反馈。我知道它是根据 unicode 中字符的代码点进行排序的。但是,将其更改为使用 localeCompare 会删除对数字进行排序的能力,而且速度也会明显变慢。
C
Christoph

除了使用自定义比较函数,您还可以使用自定义 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();

T
Tero Tolonen

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

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

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

此解决方案基于计算结果为 first evaluated expression which can be converted to true|| 运算符的行为。

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

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

last_nom 有两个步骤,first_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 可以带您走很长一段路,而无需外部库或复杂代码。它也非常有效,因为不必进行字符串解析


A
Abhishek

尝试这个,

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

最佳和简单的解决方案
对我不起作用,尝试了其他确实有效的解决方案,但这个没有。尝试按字符串排序。
J
Jamie Mason

示例用法:

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 来确定顺序吗?谢谢。
这并不完全准确,但这样考虑可能会有所帮助:传递给 array.sort 的函数对数组中的每个项目调用一次,作为名为“a”的参数。每个函数调用的返回值是与下一个项目“b”相比,项目“a”的索引(当前位置编号)应该如何改变。索引指示数组的顺序(0、1、2 等)因此,如果“a”位于索引 5 并且您返回 -1,则 5 + -1 == 4(将其移近前面) 5 + 0 == 5 (保持它在原处)等等。它遍历数组,每次比较 2 个邻居,直到它到达末尾,留下一个排序的数组。
感谢您花时间进一步解释这一点。因此,使用您的解释和 MDN Array.prototype.sort,我会告诉您我的想法:与 ab 相比,如果 a 大于 b,则将 1 添加到索引a 放在 b 后面,如果 a 小于 b,则将 a 减 1 放在 b 前面。如果 ab 相同,则将 0 添加到 a 并将其保留在原处。
a
a8m

我知道这个问题太老了,但我没有看到任何与我类似的实现。
此版本基于 Schwartzian transform idiom

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

a
artem

为什么不写短代码?

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

如果值相等怎么办?考虑到您可以返回 3 个值 - 1, -1, 0
@SomeoneSpecial 那又怎样?结果将是相同的
|| 是什么意思-1 是什么意思?
@KaleemElahi 如果我理解正确,他将其用作位掩码。如果 a.last_nom > b.last_nom THEN 1 ELSE -1。根据比较有效地向上或向下移动项目。
没有位掩码,表达式 a>b && 1|| -1 等于 a> b ? 1 : -1,运算符 && 返回第一个逻辑 false 值,运算符 || 返回第一个逻辑 true 值。
M
Martijn Pieters

使用 JavaScript 排序方法

sort 方法可以修改为使用比较函数对数字、字符串甚至对象数组等任何内容进行排序。

比较函数作为可选参数传递给 sort 方法。

此比较函数接受 2 个参数,通常称为 a 和 b。基于这 2 个参数,您可以修改排序方法以根据需要工作。

如果比较函数返回小于 0,则 sort() 方法在比 b 低的索引处对 a 进行排序。简单地说,a 将出现在 b 之前。如果比较函数返回等于 0,则 sort() 方法将元素位置保持原样。如果比较函数返回大于 0,则 sort() 方法在比 b 更大的索引处对 a 进行排序。简单地说,a 将出现在 b 之后。

使用上述概念应用于您的对象,其中 a 将是您的对象属性。

var objs = [ { first_nom: 'Lazslo', last_nom: 'Jamf' }, { first_nom: 'Pig', last_nom: 'Bodine' }, { first_nom: '海盗', last_nom: 'Prentice' } ];函数比较(a,b){如果(a.last_nom>b.last_nom)返回1;如果(a.last_nom < b.last_nom)返回 -1;返回0; } objs.sort(比较); console.log(objs) // 为了更好看,使用 console.table(objs)


C
Community

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

由于您可能会遇到像这个数组这样更复杂的数据结构,我将扩展解决方案。

TL;博士

是基于@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.name 排序,其次按 People.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;
  }
}

例子

工作示例 on JSBin


为什么?这不是原始问题的答案,“目标”可以简单地用 People.sort((a,b)=>{ return a.Name.name.localeCompare(b.Name.name) || a.Name 来解决.surname.localeCompare(b.Name.surname) })
r
ravshansbox

另一种选择:

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

默认按升序排序。


如果需要,这里提供了用于按多个字段排序的略微更改的版本:stackoverflow.com/questions/6913512/…
看起来它可能是下一个: export function generateSortFn( prop: string, reverse: boolean = false ): (...args: any) => number { return (a, b) => { return a[prop ] < b[道具] ?撤销 ? 1:-1:a[prop] > b[prop]?撤销 ? -1:1:0; }; }
同意,但在某些情况下,我不需要查看实用功能。
C
Caio Ladislau

一个简单的方法:

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

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


您可以使用 arrow functions 让代码更优雅一点:objs.sort( (a,b) => b.last_nom.toLowerCase() < a.last_nom.toLowerCase() );
这是错误的,原因与 here 解释的相同。
箭头函数不适合 ES5。大量的引擎仍然仅限于 ES5。就我而言,我发现上面的答案要好得多,因为我使用的是 ES5 引擎(由我的公司强制)
F
Francois Girard

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

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

该解决方案非常适合我进行双向排序。感谢你
c
cbdeveloper

这是我对此的看法:

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') // 删除重音符号 .replace(/[\u0300-\u036f]/g,'') // 删除变音符号 .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) => 删除 item.tempProp);返回 arr; } const rockStars = [ { name: "Axl", lastname: "Rose" }, { name: "Elthon", lastname: "John" }, { name: "Paul", lastname: "McCartney" }, { name: "Lou", lastname: "Reed" }, { name: "freddie", // 适用于小写/大写 lastname: "mercury" }, { name: "Ámy", // 适用于重音字符 lastname: "酒屋”} ]; sortByProperty(rockStars,"name"); console.log("按名称 AZ 排序:"); rockStars.forEach((item) => console.log(item.name + " " + item.lastname)); sortByProperty(rockStars,"姓氏","DESC"); console.log("\n按姓 ZA 排序:"); rockStars.forEach((item) => console.log(item.lastname + ", " + item.name));


如果列表包含大小写字符组合的名称,则不起作用
@AnkeshPandey 感谢您指出这一点。我已经修好了。
B
Ben Carp

警告!不建议使用此解决方案,因为它不会导致排序数组。它留在这里供将来参考,因为这个想法并不罕见。

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

实际上它似乎不起作用,不得不使用公认的答案。它没有正确排序。
N
Not A Bot

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

Array.prototype.sortBy = function() { function _sortByAttr(attr) { var sortOrder = 1; if (attr[0] == "-") { sortOrder = -1; attr = attr.substr(1); } 返回函数(a,b){ var 结果 =(a[attr] < b[attr])? -1 : (a[attr] > b[attr]) ? 1:0;返回结果 * 排序顺序; } } function _getSortFunc() { if (arguments.length == 0) { throw "Array.sortBy() 不允许零长度参数"; } var args = 参数; return function(a, b) { for (var result = 0, i = 0; result == 0 && i < args.length; i++) { result = _sortByAttr(args[i])(a, b); } 返回结果; } } 返回 this.sort(_getSortFunc.apply(null, arguments)); } 用法: // 打印对象的实用程序 Array.prototype.print = function(title) { console.log("****************************** ***********************************************"); console.log("****" + 标题); console.log("*************************************************** ******************************"); for (var i = 0; i < this.length; i++) { console.log("Name: " + this[i].FirstName, this[i].LastName, "Age:" + this[i].Age ); } } // 设置示例数据 var arrObj = [{ FirstName: "Zach", LastName: "Emergency", Age: 35 }, { FirstName: "Nancy", LastName: "Nurse", Age: 27 }, { FirstName: “埃塞尔”,姓:“紧急”,年龄:42 },{ 名字:“尼娜”,姓:“护士”,年龄:48 },{ 名字:“安东尼”,姓:“紧急”,年龄:44 } , { FirstName: "Nina", LastName: "Nurse", Age: 32 }, { FirstName: "Ed", LastName: "Emergency", Age: 28 }, { FirstName: "Peter", LastName: "Physician",年龄:58 },{ 名字:“Al”,姓氏:“紧急”,年龄:51 },{ 名字:“Ruth”,姓氏:“注册”,年龄:62 },{ 名字:“Ed”,姓氏: “紧急情况”,年龄:38 },{名字:“Tammy”,姓氏:“Triage”,年龄:29 },{名字:“Alan”,姓氏:“紧急情况”,年龄:60 },{名字:“Nina” ", 姓: "护士", 年龄: 54 } ]; //单元测试 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");


谢谢你的主意!顺便说一句,请不要鼓励人们更改 Array Prototype(请参阅示例末尾的警告)。
B
Behnam Shomali

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

B
Bob Stein

给定原始示例:

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) 得到普遍支持,如果 ab 分别返回 -1,0,1。

||在最后一行中,last_nom 优先于 first_nom。

减法适用于数字字段: var age_order = left.age - right.age;

取反顺序,返回 -last_nom_order || -first_nom_order || -年龄顺序;


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

请考虑编辑您的帖子,以添加更多关于您的代码的作用以及它为什么会解决问题的解释。一个大部分只包含代码的答案(即使它正在工作)通常不会帮助 OP 理解他们的问题。
S
Sridhar Sg

使用 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)