【问题标题】:Sort array of objects by string property value按字符串属性值对对象数组进行排序
【发布时间】:2021-06-27 03:37:10
【问题描述】:

我有一个 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) { 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

标签: javascript arrays sorting comparison


【解决方案1】:

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

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

【讨论】:

  • 或内联: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)
  • 请写短代码objs.sort((a, b) =&gt; a.last_nom &gt; b.last_nom &amp;&amp; 1 || -1)
【解决方案2】:

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

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

【讨论】:

【解决方案3】:

在 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。您可以使用标准的&gt; 运算符 - 就像@muasif80 的答案中提到的那样 - stackoverflow.com/a/67992215/6908282
【解决方案4】:

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库“下划线”:&lt;script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"&gt; &lt;/script&gt;
  • 对于喜欢那个的人也可以在Lodash 中找到
  • 在 lodash 中这将是相同的:var sortedObjs = _.sortBy( objs, 'first_nom' ); 或者如果你想要它以不同的顺序:var sortedObjs = _.orderBy( objs, ['first_nom'],['dsc'] );
【解决方案5】:

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

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 搜索了各种sn-ps 代码。我只是没有找到我需要的信息。
【解决方案6】:

截至 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可以用于不同时间的数据。例如,正则表达式可用于比较每对相邻的 strings.
  • @0leg 我想在这里看到一个使用正则表达式以这种方式比较字符串的示例。
  • 如果需要按ID排序的话,这个实现还是挺不错的。是的,您建议使用正则表达式来比较相邻的字符串,这会使解决方案更加复杂,而如果将正则表达式与给定的解决方案一起使用,则此简化版本的目的将是否则。简单是最好的。
【解决方案7】:

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

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)
  • @codehuntr 感谢您的纠正。但我想与其制作排序函数来进行这种敏感化,不如制作一个单独的函数来修复数据类型。因为排序函数不能不知道哪个属性会包含什么样的数据。 :)
  • 非常好。反转箭头以获得 asc/desc 效果。
  • 从不建议修改核心对象原型的解决方案
【解决方案8】:

区分大小写

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

不区分大小写

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

注意

如果顺序没有变化(在相同字符串的情况下),则条件&gt; 将失败并返回-1。但如果字符串相同,则返回 1 或 -1 将导致正确的输出

另一种选择是使用&gt;= 运算符而不是&gt;

【讨论】:

  • 区分大小写的方法是一种很好的速记方式——尤其是当值是数字或日期时。
  • 提示:如果你想颠倒顺序,你可以简单地交换-11,例如:从1 : -1-1 : 1
  • 如何改变(a, b) to (b, a) :)
  • 是的,这也有效。我只是发现交换 1-1 更直接和合乎逻辑。
【解决方案9】:

不正确的旧答案:

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 转换为1false 转换为0

【讨论】:

  • 它可以工作,但由于某种原因结果不稳定
  • @AO17 不会的。你不能减去字符串。
  • 应该这样做:arr.sort((a, b) =&gt; a.name &lt; b.name ? -1 : (a.name &gt; b.name ? 1 : 0))
  • @Jean-FrançoisBeauchamp,您的解决方案效果很好,而且效果更好。
  • 为什么arr.sort((a, b) =&gt; a.name &gt; b.name ? 1 : -1 不起作用?对于我已经测试过的字符串,这很好用。如果您想不区分大小写,请使用 a.name.toLowerCase()b.name.toLowerCase()
【解决方案10】:

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

【讨论】:

    【解决方案11】:

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

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

    这个方法类似于_.sortBy,除了它允许指定要排序的迭代对象的排序顺序。如果未指定 orders,则所有值都按升序排序。否则,指定相应值的降序排列顺序为“desc”,升序排列顺序为“asc”。

    参数

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

    退货

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

    【讨论】:

      【解决方案12】:

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

      const objs = [ 
        { first_nom: 'Lazslo', last_nom: 'Jamf'     },
        { first_nom: 'Pig',    last_nom: 'Bodine'   },
        { first_nom: 'Pirate', last_nom: 'Prentice' }
      ];
      
      const sortBy = fn => {
        const cmp = (a, b) => -(a < b) || +(a > b);
        return (a, b) => cmp(fn(a), fn(b));
      };
      
      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。每当我们收到两个对象,例如

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

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

      fn = o => o.last_nom
      

      我们可以将比较函数扩展为(a, b) =&gt; 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" (&lt;=&gt;) operator

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

      var objs = [ 
        { first_nom: 'Lazslo', last_nom: 'Jamf'     },
        { first_nom: 'Pig',    last_nom: 'Bodine'   },
        { first_nom: 'Pirate', last_nom: 'Prentice' }
      ];
      
      function sortBy(fn) {
        function cmp(a, b) { return -(a < b) || +(a > b); }
        return function (a, b) { return 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 &lt; fb) || +(fa &gt; fb) 的简写是错误的。那就是将多条语句压缩成一行代码。使用if 语句编写的替代方案将更具可读性,同时仍然相当简洁。我认为为了漂亮而牺牲可读性是错误的。
      • @MSOACC 感谢您的意见,但我非常不同意。其他语言实现了执行相同比较的three-way comparison operator,因此在概念上将其视为fa &lt;=&gt; fb
      • 嘿帕特里克,我喜欢你的回答,但它只适用于英文字符 (const cmp = (a, b) =&gt; -(a &lt; b) || +(a &gt; b);) 想想["ä", "a", "c", "b"].sort(cmp) => ["a", "b", "c", "ä"],其中ä 被推到最后。相反,您可能应该将比较功能更新为:const cmp = (a, b) =&gt; a.localeCompare(b); => ["a", "ä", "b", "c"] 干杯并感谢您的回答;-)
      • @rjanjic 感谢您的反馈。我知道它是根据 unicode 中字符的代码点进行排序的。但是,将其更改为使用 localeCompare 会删除对数字进行排序的能力,并且速度也会明显变慢。
      【解决方案13】:

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

      【讨论】:

        【解决方案14】:

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

        【讨论】:

          【解决方案15】:

          示例用法:

          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 并保留原处。
          【解决方案16】:

          试试这个,

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

          【讨论】:

          • 最简单的解决方案
          【解决方案17】:

          我知道这个问题太老了,但我没有看到任何类似于我的实现。
          此版本基于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'));
          

          【讨论】:

            【解决方案18】:

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

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

            TL;DR

            基于@ege-Özcan的很可爱的answer的更可插拔的版本。

            问题

            我遇到了以下问题,无法更改。我也不想暂时压平物体。我也不想使用下划线/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) })
            【解决方案19】:

            另一种选择:

            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 ) => { 返回 a[prop] b[prop]?逆转 ? -1:1:0; }; }
            • 同意,但在某些情况下,我不需要查看实用功能。
            【解决方案20】:

            一个简单的方法:

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

            请注意'.toLowerCase()' 是防止错误所必需的 在比较字符串时。

            【讨论】:

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

            警告!
            不推荐使用此解决方案,因为它不会产生排序数组。它留在这里以备将来参考,因为这个想法并不罕见。

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

            【讨论】:

            • 实际上它似乎不起作用,不得不使用接受的答案。排序不正确。
            【解决方案22】:

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

            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
            

            【讨论】:

            • 这个解决方案非常适合我进行双向排序。谢谢你
            【解决方案23】:

            这是我的看法:

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

            适用于重音字符且不区分大小写

            注意:它对ORIGINAL数组进行排序并返回。

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

            【讨论】:

            • 如果列表包含大小写组合的名称,则不起作用
            • @AnkeshPandey 感谢您指出这一点。我已经修好了。
            【解决方案24】:

            你为什么不写短代码?

            objs.sort((a, b) => a.last_nom > b.last_nom && 1 || -1)
            

            【讨论】:

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

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

            【讨论】:

              【解决方案26】:

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

              【讨论】:

              • 感谢您的建议!顺便说一句,请不要鼓励人们更改 Array Prototype(请参阅示例末尾的警告)。
              【解决方案27】:
              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 理解他们的问题。
              【解决方案28】:

              您可以使用可重复使用的排序功能。

              Array.prototype.order = function (prop, methods = {}) {
                  if (prop?.constructor == Object) {
                      methods = prop;
                      prop = null;
                  }
                  const [orderType_a, orderType_b] = methods.reverse ? [1, -1] : [-1, 1];
              
                  const $ = x => prop
                      ? methods.insensitive
                          ? String(x[prop]).toLowerCase()
                          : x[prop]
                      : methods.insensitive
                          ? String(x).toLowerCase()
                          : x;
              
                  const fn = (a, b) => $(a) < $(b) ? orderType_a : $(b) < $(a) ? orderType_b : 0;
                  return this.sort(fn);
              };
              

              它可用于对数组中的ArrayObject进行排序

              let items = [{ x: "Z" }, 3, "1", "0", 2, { x: "a" }, { x: 0 }];
              items
                  .order("x", { insensitive: 1 })
                  // [ { x: 0 }, { x: 'a' }, 3, '1', '0', 2, { x: 'Z' } ]
                  .order({ reverse: 1 })
                  // [ { x: 0 }, { x: 'a' }, 3, 2, { x: 'Z' }, '1', '0' ]
                  .sort(x => typeof x == "string" || typeof x == "number" ? -1 : 0)
                  // [ '0', '1', 2, 3, { x: 0 }, { x: 'a' }, { x: 'Z' } ]
              

              第一个(可选) > 对包含在数组中的对象进行排序。
              第二个是方法 > { reverse: any, insensitive: any }

              【讨论】:

                【解决方案29】:

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

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

                试试这个例子at jsFiddle

                【讨论】:

                  【解决方案30】:

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

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

                  【讨论】:

                  • 如何在js中实现快速排序是一个简单的解决方案?简单的算法,但不是简单的解决方案。
                  • 这很简单,因为它不使用任何外部库,也不改变对象的原型。在我看来,代码的长度对代码的复杂度没有直接影响
                  • 好吧,让我换个说法:如何重新发明轮子是一个简单的解决方案?
                  猜你喜欢
                  相关资源
                  最近更新 更多