我有一个JavaScript对象数组:

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

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

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


当前回答

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

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

其中fn1,fn2。。。是返回[-1,0,1]的排序函数。这导致“按fn1排序”和“按fn2排序”,这在SQL中相当于ORDERBY。

此解决方案基于||运算符的行为,该运算符的求值结果为第一个可转换为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可以在没有外部库或复杂代码的情况下走很长的路。它也非常有效,因为不需要进行字符串解析。

其他回答

这是一个简单的问题。我不知道为什么人们会有如此复杂的解决方案。

一个简单的排序函数(基于快速排序算法):

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

排序(更多)复杂的对象阵列

由于您可能会遇到类似于此阵列的更复杂的数据结构,因此我将扩展解决方案。

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

Goal

目标是主要按People.Name.Name排序,其次按People.Name.surname排序

障碍

现在,在基本解决方案中,使用括号表示法来计算要动态排序的财产。不过,在这里,我们还必须动态地构造括号表示法,因为您可能会期望像People['Name.Name']这样的符号会起作用,但这不起作用。

另一方面,简单地做人物['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的工作示例

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

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的幸运者,ES6允许扩展本机对象:

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

这将实现:

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

您也可以使用自定义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();

此排序功能可用于所有对象排序:

对象deepObject(深度对象)数字数组

您还可以通过传递1,-1作为参数进行升序或降序排序。

Object.defineProperty(Object.prototype,'deepVal'{可枚举:false,可写:true,值:函数(propertyChain){var level=propertyChain.split('.');父项=此项;对于(var i=0;i<levels.length;i++){if(!parent[levels[i]])返回未定义;parent=父[级别[i]];}返回父项;}});函数dynamicSortAll(属性,sortOrders=1){/**默认排序为升序。如果你需要按降序排序传递-1作为参数**/var sortOrder=sortOrders;返回函数(a,b){var result=(属性?((a.deepVal(属性)>b.deepVal(属性))?1:(a.deepVal(属性)<b.deepVal(属性))-1:0):((a>b)?1:(a<b)-1 : 0))返回结果*sortOrder;}}深度对象=[{a: {a:1,b:2,c:3},b: {a:4,b:5,c:6}},{a: {a:3,b:2,c:1},b: {a:6,b:5,c:4}}];let deepobjResult=deepObj.sort(dynamicSortAll('a.a',1))console.log('deepobjResult:'+JSON.stringify(deepojResult))变量obj=[{first_nom:'Lazslo',last_nom:'Jamf'},{first_nom:'猪',last_nom:'Bodine'},{first_nom:'海盗',last_nom:'Prentice'}];let objResult=obj.sort(dynamicSortAll('last_nom',1))console.log('objResult:'+JSON.stringify(objResult))var numericObj=[1,2,3,4,5,6]let numResult=numericObj.sort(dynamicSortAll(null,-1))console.log('numResult:'+JSON.stringify(numResult))let stringSortResult='helloworld'.split('').sort(dynamicSortAll(null,1))console.log('stringSortResult:'+JSON.stringify(stringSortResult))let uniqueStringOrger=[…new Set(stringSortResult)];console.log('uniqueStringOrger:'+JSON.stringify(uniqueStringOrger))