Reputation: 935
From what it said, it sounds very simple, but I wasn't succeeded to achieve the perfect result in Chrome (I did it in IE)
So I have an object:
var objs=[
{ name: "water", type: "cold" },
{ name: "tea", type: "hot" },
{ name: "coffee", type: "hot" },
{ name: "choco", type: "cold" }
];
I sorted it by name, ascending and I got the expected result:
I got the same result both in IE and Chrome,
Then, I sort it again by type, ascending, and my expectation:
But I only got the expected result in IE, while in Chrome the result was almost the same except that "water" came at the top (it ruins the first sorting)
any general approach idea? (I need to keep it one function call per field, not sorting by multiple fields in one function at the same time)
My sort method was like this:
function SortBy(key,Asc,method){
var GetKey=function(x) {return method ? method(x[key]) : x[key] };
return function(a,b){
var A=GetKey(a), B=GetKey(b);
return ((A<B) ? -1 : ((A>B) ? 1 : 0)) * [-1,1][+!!Asc];
}
}
var sortedObjects=Clone(objs); //clone the current objs
sortedObjects.sort(SortBy("name" , true, function(x){
if (typeof(x)=="string") return x.toLowerCase(); else return x;
}));
objs=sortedObjects; //got the first result
sortedObjects.sort(SortBy("type",true,function(x){
if (typeof(x)=="string") return x.toLowerCase(); else return x;
}));
objs=sortedObjects;
EDITED here is the fiddle (the problem is the "Water"): DEMO
Upvotes: 0
Views: 117
Reputation: 66364
If we read the ES5 spec §15.4.4.11 Array.prototype.sort, the very second sentence states
The sort is not necessarily stable (that is, elements that compare equal do not necessarily remain in their original order)
That is to say, in JavaScript, if the comparator says two things are equal (by giving 0
), it doesn't matter if they get swapped or not and hence in some environments they may swapped due to some other reason such as their hashes or just the implementation.
i.e. The result you experience is a valid result of sort
as defined by the spec and repeating the same sort
may even give different results each time
Hopefully I won't get downvoted too heavily for this, but if you do it as one function then you don't need to iterate through the array as many times and you'll avoid your unexpected result
/* list = multiSort(list [, obj, ...])
* where e.g. obj = {
* key: "keyToLookAt",
* optional comparator: function (a, b) {return a<b || ((a===b)-1);},
* optional ascending: default true
* }
*/
var multiSort = (function () {
function generateSorter(key, comparator, ascending) {
ascending = 2 * !!ascending - 1;
if (comparator) {
return function sorter(a, b) {
return ascending * comparator(a[key], b[key]);
};
}
return function sorter(a, b) {
a = a[key];
b = b[key];
return ascending * (a < b ? -1 : a > b ? 1 : 0);
};
}
function sortManager(sorters) {
return function (a, b) {
var ret, i = 0;
for (; i < sorters.length; ++i) {
ret = sorters[i](a, b);
if (ret !== 0) return ret;
}
return 0;
};
}
function multiSort(list) {
var i = 1, sorters = [];
for (; i < arguments.length; ++i) {
sorters.push(generateSorter(
arguments[i].key,
arguments[i].comparator,
'ascending' in arguments[i] ? arguments[i].ascending : 1
));
}
list.sort(sortManager(sorters));
return list;
}
return multiSort;
}());
And then when using it, the "most strict" sort rule goes first
multiSort(
[
{ name: "water", type: "cold" },
{ name: "tea", type: "hot" },
{ name: "coffee", type: "hot" },
{ name: "choco", type: "cold" }
],
{key: 'type'},
{key: 'name'}
);
/*
[
{ "name": "choco" , "type": "cold" },
{ "name": "water" , "type": "cold" },
{ "name": "coffee", "type": "hot" },
{ "name": "tea" , "type": "hot" }
]
*/
Upvotes: 1
Reputation: 1184
I created a CodePen to test and work fine... I write the same idea in more readable code
function SortBy(key, isAsc, fn){
fn = fn || function (x){ return x; };
return function(objA, objB){
var a = fn(objA[key]);
var b = fn(objB[key]);
var isGreater = isAsc ? 1 : -1;
var isLesser = -isGreater;
var isSame = 0;
if( a > b ) return isGreater;
if( a == b ) return isSame;
if( a < b ) return isLesser;
return -1; //to down the undefined
}
}
http://codepen.io/anon/pen/sGgDq edited and tested with a lot of data
p.s.: I used codepen because jsfiddle not open in my work
Upvotes: 0