# 排序（更多）复杂的对象数组

## 问题

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

## 解

``````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上的工作示例

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

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

EgeÖzcan代码的额外参数

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

Given the original example:

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

Sort by multiple fields:

``````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)` is universally supported and returns -1,0,1 if `a<b` , `a==b` , `a>b` respectively.
• `||` in the last line gives `last_nom` priority over `first_nom` .
• Subtraction works on numeric fields: `var age_order = left.age - right.age;`
• Negate to reverse order, `return -last_nom_order || -first_nom_order || -age_order;`

So here is one sorting algorithm which can sort in any order , throughout array of any kind of objects , without the restriction of datatype comparison ( ie Number , String )

``````function smoothSort(items,prop,reverse) {
var length = items.length;
for (var i = (length - 1); i >= 0; i--) {
//Number of passes
for (var j = (length - i); j > 0; j--) {
if(reverse){
if (items[j][prop] > items[j - 1][prop]) {
//Swap the numbers
var tmp = items[j];
items[j] = items[j - 1];
items[j - 1] = tmp;
}
}

if(!reverse){
if (items[j][prop] < items[j - 1][prop]) {
//Swap the numbers
var tmp = items[j];
items[j] = items[j - 1];
items[j - 1] = tmp;
}
}
}
}

return items;
}
``````
• the first argument items is the array of objects ,

• prop is the key of the object on which you want to sort ,

• reverse is a boolean parameter which on being true results in Ascending order and in false it returns descending order.

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

``````var o = [
{ Name: 'Lazslo', LastName: 'Jamf'     },
{ Name: 'Pig',    LastName: 'Bodine'   },
{ Name: 'Pirate', LastName: 'Prentice' },
{ Name: 'Pag',    LastName: 'Bodine'   }
];

// Original
o.each(function (a, b) { console.log(a, b); });
/*
0 Object {Name: "Lazslo", LastName: "Jamf"}
1 Object {Name: "Pig", LastName: "Bodine"}
2 Object {Name: "Pirate", LastName: "Prentice"}
3 Object {Name: "Pag", LastName: "Bodine"}
*/

// Sort By LastName ASC, Name ASC
o.sortBy('LastName', 'Name').each(function(a, b) { console.log(a, b); });
/*
0 Object {Name: "Pag", LastName: "Bodine"}
1 Object {Name: "Pig", LastName: "Bodine"}
2 Object {Name: "Lazslo", LastName: "Jamf"}
3 Object {Name: "Pirate", LastName: "Prentice"}
*/

// Sort by LastName ASC and Name ASC
o.sortBy('LastName'.asc, 'Name'.asc).each(function(a, b) { console.log(a, b); });
/*
0 Object {Name: "Pag", LastName: "Bodine"}
1 Object {Name: "Pig", LastName: "Bodine"}
2 Object {Name: "Lazslo", LastName: "Jamf"}
3 Object {Name: "Pirate", LastName: "Prentice"}
*/

// Sort by LastName DESC and Name DESC
o.sortBy('LastName'.desc, 'Name'.desc).each(function(a, b) { console.log(a, b); });
/*
0 Object {Name: "Pirate", LastName: "Prentice"}
1 Object {Name: "Lazslo", LastName: "Jamf"}
2 Object {Name: "Pig", LastName: "Bodine"}
3 Object {Name: "Pag", LastName: "Bodine"}
*/

// Sort by LastName DESC and Name ASC
o.sortBy('LastName'.desc, 'Name'.asc).each(function(a, b) { console.log(a, b); });
/*
0 Object {Name: "Pirate", LastName: "Prentice"}
1 Object {Name: "Lazslo", LastName: "Jamf"}
2 Object {Name: "Pag", LastName: "Bodine"}
3 Object {Name: "Pig", LastName: "Bodine"}
*/
``````

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

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

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

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

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

})
``````

``````obj = [
{
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 }
}];
``````

``````Object.defineProperty(Object.prototype, 'deepVal', {
enumerable: false,
writable: true,
value: function (propertyChain) {
var levels = propertyChain.split('.');
parent = this;
for (var i = 0; i < levels.length; i++) {
if (!parent[levels[i]])
return undefined;
parent = parent[levels[i]];
}
return parent;
}
});
``````

``````return function (a,b) {
var result = ((a.deepVal(property) > b.deepVal(property)) - (a.deepVal(property) < b.deepVal(property)));
return result * sortOrder;
}
``````

``````obj.sortBy('a.a');
``````

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

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

``````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}
*/
function 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.
var 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.
var moveLarger = reverse ? -1 : 1;

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

}
``````

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

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

``````// Sort Array of Objects

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

// Property to Sort By
var args = "last_nom";

// Function to Sort the Data by given Property
function sortByProperty(property) {
return function (a, b) {
var sortStatus = 0,
aProp = a[property].toLowerCase(),
bProp = b[property].toLowerCase();
if (aProp < bProp) {
sortStatus = -1;
} else if (aProp > bProp) {
sortStatus = 1;
}
return sortStatus;
};
}

// Implementation
var sortedArray = booksArray.sort(sortByProperty(args));

console.log("sortedArray: " + JSON.stringify(sortedArray) );
``````

Console log output:

``````"sortedArray:
[{"first_nom":"Pig","last_nom":"Bodine"},
{"first_nom":"Lazslo","last_nom":"Jamf"},
{"first_nom":"Pirate","last_nom":"Prentice"}]"
``````

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