可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a multidimensional array. The primary array is an array of
[publicationID][publication_name][ownderID][owner_name]
What I am trying to do is sort the array by owner_name
and then by publication_name
. I know in JavaScript you have Array.sort()
, into which you can put a custom function, in my case i have:
function mysortfunction(a, b) {
var x = a[3].toLowerCase();
var y = b[3].toLowerCase();
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
}
This is fine for just sorting on the one column, namely owner_name, but how do I modify it to sort on owner_name
, then publication_name
?
回答1:
If owner names differ, sort by them. Otherwise, use publication name for tiebreaker.
function mysortfunction(a, b) {
var o1 = a[3].toLowerCase();
var o2 = b[3].toLowerCase();
var p1 = a[1].toLowerCase();
var p2 = b[1].toLowerCase();
if (o1 < o2) return -1;
if (o1 > o2) return 1;
if (p1 < p2) return -1;
if (p1 > p2) return 1;
return 0;
}
回答2:
I think what you\'re looking for is thenBy.js: https://github.com/Teun/thenBy.js
It allows you to use the standard Array.sort, but with firstBy().thenBy().thenBy()
style.
An example can be seen here.
回答3:
Came across a need to do SQL-style mixed asc and desc object array sorts by keys.
kennebec\'s solution above helped me get to this:
Array.prototype.keySort = function(keys) {
keys = keys || {};
// via
// https://stackoverflow.com/questions/5223/length-of-javascript-object-ie-associative-array
var obLen = function(obj) {
var size = 0, key;
for (key in obj) {
if (obj.hasOwnProperty(key))
size++;
}
return size;
};
// avoiding using Object.keys because I guess did it have IE8 issues?
// else var obIx = function(obj, ix){ return Object.keys(obj)[ix]; } or
// whatever
var obIx = function(obj, ix) {
var size = 0, key;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (size == ix)
return key;
size++;
}
}
return false;
};
var keySort = function(a, b, d) {
d = d !== null ? d : 1;
// a = a.toLowerCase(); // this breaks numbers
// b = b.toLowerCase();
if (a == b)
return 0;
return a > b ? 1 * d : -1 * d;
};
var KL = obLen(keys);
if (!KL)
return this.sort(keySort);
for ( var k in keys) {
// asc unless desc or skip
keys[k] =
keys[k] == \'desc\' || keys[k] == -1 ? -1
: (keys[k] == \'skip\' || keys[k] === 0 ? 0
: 1);
}
this.sort(function(a, b) {
var sorted = 0, ix = 0;
while (sorted === 0 && ix < KL) {
var k = obIx(keys, ix);
if (k) {
var dir = keys[k];
sorted = keySort(a[k], b[k], dir);
ix++;
}
}
return sorted;
});
return this;
};
sample usage:
var obja = [
{USER:\"bob\", SCORE:2000, TIME:32, AGE:16, COUNTRY:\"US\"},
{USER:\"jane\", SCORE:4000, TIME:35, AGE:16, COUNTRY:\"DE\"},
{USER:\"tim\", SCORE:1000, TIME:30, AGE:17, COUNTRY:\"UK\"},
{USER:\"mary\", SCORE:1500, TIME:31, AGE:19, COUNTRY:\"PL\"},
{USER:\"joe\", SCORE:2500, TIME:33, AGE:18, COUNTRY:\"US\"},
{USER:\"sally\", SCORE:2000, TIME:30, AGE:16, COUNTRY:\"CA\"},
{USER:\"yuri\", SCORE:3000, TIME:34, AGE:19, COUNTRY:\"RU\"},
{USER:\"anita\", SCORE:2500, TIME:32, AGE:17, COUNTRY:\"LV\"},
{USER:\"mark\", SCORE:2000, TIME:30, AGE:18, COUNTRY:\"DE\"},
{USER:\"amy\", SCORE:1500, TIME:29, AGE:19, COUNTRY:\"UK\"}
];
var sorto = {
SCORE:\"desc\",TIME:\"asc\", AGE:\"asc\"
};
obja.keySort(sorto);
yields the following:
0: { USER: jane; SCORE: 4000; TIME: 35; AGE: 16; COUNTRY: DE; }
1: { USER: yuri; SCORE: 3000; TIME: 34; AGE: 19; COUNTRY: RU; }
2: { USER: anita; SCORE: 2500; TIME: 32; AGE: 17; COUNTRY: LV; }
3: { USER: joe; SCORE: 2500; TIME: 33; AGE: 18; COUNTRY: US; }
4: { USER: sally; SCORE: 2000; TIME: 30; AGE: 16; COUNTRY: CA; }
5: { USER: mark; SCORE: 2000; TIME: 30; AGE: 18; COUNTRY: DE; }
6: { USER: bob; SCORE: 2000; TIME: 32; AGE: 16; COUNTRY: US; }
7: { USER: amy; SCORE: 1500; TIME: 29; AGE: 19; COUNTRY: UK; }
8: { USER: mary; SCORE: 1500; TIME: 31; AGE: 19; COUNTRY: PL; }
9: { USER: tim; SCORE: 1000; TIME: 30; AGE: 17; COUNTRY: UK; }
keySort: { }
(using a print function from here)
here is a jsbin example.
edit: cleaned up and posted as mksort.js on github.
回答4:
This is handy for alpha sorts of all sizes.
Pass it the indexes you want to sort by, in order, as arguments.
Array.prototype.deepSortAlpha= function(){
var itm, L=arguments.length, order=arguments;
var alphaSort= function(a, b){
a= a.toLowerCase();
b= b.toLowerCase();
if(a== b) return 0;
return a> b? 1:-1;
}
if(!L) return this.sort(alphaSort);
this.sort(function(a, b){
var tem= 0, indx=0;
while(tem==0 && indx<L){
itm=order[indx];
tem= alphaSort(a[itm], b[itm]);
indx+=1;
}
return tem;
});
return this;
}
var arr= [[ \"Nilesh\",\"Karmshil\"], [\"Pranjal\",\"Deka\"], [\"Susants\",\"Ghosh\"],
[\"Shiv\",\"Shankar\"], [\"Javid\",\"Ghosh\"], [\"Shaher\",\"Banu\"], [\"Javid\",\"Rashid\"]];
arr.deepSortAlpha(1,0);
回答5:
A good way to sort on many fields that are strings is to use toLocaleCompare
and the boolean operator ||
.
Something like:
// Sorting record releases by name and then by title.
releases.sort((oldRelease, newRelease) => {
const compareName = oldRelease.name.localeCompare(newRelease.name);
const compareTitle = oldRelease.title.localeCompare(newRelease.title);
return compareName || compareTitle;
})
If you wanted to sort on more fields, you could simply chain them off the return statement with more boolean operators.
回答6:
You could concat the 2 variables together into a sortkey and use that for your comparison.
list.sort(function(a,b){
var aCat = a.var1 + a.var2;
var bCat = b.var1 + b.var2;
return (aCat > bCat ? 1 : aCat < bCat ? -1 : 0);
});
回答7:
I suggest to use a built in comparer and chain the wanted sort order with logical or ||
.
function customSort(a, b) {
return a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]);
}
Working example:
var array = [
[0, \'Aluminium\', 0, \'Francis\'],
[1, \'Argon\', 1, \'Ada\'],
[2, \'Brom\', 2, \'John\'],
[3, \'Cadmium\', 3, \'Marie\'],
[4, \'Fluor\', 3, \'Marie\'],
[5, \'Gold\', 1, \'Ada\'],
[6, \'Kupfer\', 4, \'Ines\'],
[7, \'Krypton\', 4, \'Joe\'],
[8, \'Sauerstoff\', 3, \'Marie\'],
[9, \'Zink\', 5, \'Max\']
];
array.sort(function (a, b) {
return a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]);
});
document.write(\'<pre>\');
array.forEach(function (a) {
document.write(JSON.stringify(a) + \'<br>\');
});
回答8:
I was working with ng-grid
and needed to to multiple column sorting on an array of records returned from an API, so I came up with this nifty, dynamic multi-sort function.
First of all, ng-grid
fires an \"event\" for \"ngGridSorted\" and passes this structure back, describing the sort:
sortData = {
columns: DOM Element,
directions: [], //Array of string values desc or asc. Each index relating to the same index of fields
fields: [], //Array of string values
};
So I built a function that will dynamically generate a sort function based on the sortData
as shown above (Don\'t be scared by the scroll bar! It\'s only about 50 lines long! Also, I\'m sorry about the slop. It prevented a horizontal scrollbar!):
function SortingFunction(sortData)
{
this.sortData = sortData;
this.sort = function(a, b)
{
var retval = 0;
if(this.sortData.fields.length)
{
var i = 0;
/*
Determine if there is a column that both entities (a and b)
have that are not exactly equal. The first one that we find
will be the column we sort on. If a valid column is not
located, then we will return 0 (equal).
*/
while( ( !a.hasOwnProperty(this.sortData.fields[i])
|| !b.hasOwnProperty(this.sortData.fields[i])
|| (a.hasOwnProperty(this.sortData.fields[i])
&& b.hasOwnProperty(this.sortData.fields[i])
&& a[this.sortData.fields[i]] === b[this.sortData.fields[i]])
) && i < this.sortData.fields.length){
i++;
}
if(i < this.sortData.fields.length)
{
/*
A valid column was located for both entities
in the SortData. Now perform the sort.
*/
if(this.sortData.directions
&& i < this.sortData.directions.length
&& this.sortData.directions[i] === \'desc\')
{
if(a[this.sortData.fields[i]] > b[this.sortData.fields[i]])
retval = -1;
else if(a[this.sortData.fields[i]] < b[this.sortData.fields[i]])
retval = 1;
}
else
{
if(a[this.sortData.fields[i]] < b[this.sortData.fields[i]])
retval = -1;
else if(a[this.sortData.fields[i]] > b[this.sortData.fields[i]])
retval = 1;
}
}
}
return retval;
}.bind(this);
}
I then sort the results of my API (results
) like so:
results.sort(new SortingFunction(sortData).sort);
I hope somebody else enjoys this solution as much as I do! Thanks!
回答9:
I found multisotr. This is simple, powerfull and small library for multiple sorting. I was need to sort an array of objects with dynamics sorting criteria:
const criteria = [\'name\', \'speciality\']
const data = [
{ name: \'Mike\', speciality: \'JS\', age: 22 },
{ name: \'Tom\', speciality: \'Java\', age: 30 },
{ name: \'Mike\', speciality: \'PHP\', age: 40 },
{ name: \'Abby\', speciality: \'Design\', age: 20 },
]
const sorted = multisort(data, criteria)
console.log(sorted)
<script src=\"https://cdn.rawgit.com/peterkhayes/multisort/master/multisort.js\"></script>
This library more mutch powerful, that was my case. Try it.
回答10:
function multiSort() {
var args =$.makeArray( arguments ),
sortOrder=1, prop=\'\', aa=\'\', b=\'\';
return function (a, b) {
for (var i=0; i<args.length; i++){
if(args[i][0]===\'-\'){
prop=args[i].substr(1)
sortOrder=-1
}
else{sortOrder=1; prop=args[i]}
aa = a[prop].toLowerCase()
bb = b[prop].toLowerCase()
if (aa < bb) return -1 * sortOrder;
if (aa > bb) return 1 * sortOrder;
}
return 0
}
}
empArray.sort(multiSort( \'lastname\',\'firstname\')) Reverse with \'-lastname\'
回答11:
I had a similar problem while displaying memory pool blocks from the output of some virtual DOM h-functions composition. Basically I faced to the same problem as sorting multi-criteria data like scoring results from players around the world.
I have noticed that multi-criteria sorting is:
- sort by the first column
- if equal, sort by the second
- if equal, sort by the third
- etc... nesting and nesting if-else
And if you don\'t care, you could fail quickly in a if-else nesting hell... like callback hell of promises...
What about if we write a \"predicate\" function to decide if which part of alternative using ? The predicate is simply :
// useful for chaining test
const decide = (test, other) => test === 0 ? other : test
Now after having written your classifying tests (byCountrySize, byAge, byGameType, byScore, byLevel...) whatever who need, you can weight your tests (1 = asc, -1 = desc, 0 = disable), put them in an array, and apply a reducing \'decide\' function like this:
const multisort = (s1, s2) => {
const bcs = -1 * byCountrySize(s1, s2) // -1 = desc
const ba = 1 *byAge(s1, s2)
const bgt = 0 * byGameType(s1, s2) // 0 = doesn\'t matter
const bs = 1 * byScore(s1, s2)
const bl = -1 * byLevel(s1, s2) // -1 = desc
// ... other weights and criterias
// array order matters !
return [bcs, ba, bgt, bs, bl].reduce((acc, val) => decide(val, acc), 0)
}
// invoke [].sort with custom sort...
scores.sort(multisort)
And voila ! It\'s up to you to define your own criterias / weights / orders... but you get the idea. Hope this helps !
EDIT:
* ensure that there is a total sorting order on each column
* be aware of not having dependencies between columns orders, and no circular dependencies
if, not, sorting can be unstable !
回答12:
Try this:
t.sort( (a,b)=> a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]) );
let t = [
//[publicationID, publication_name, ownderID, owner_name ]
[1, \'ZBC\', 3, \'John Smith\'],
[2, \'FBC\', 5, \'Mike Tyson\'],
[3, \'ABC\', 7, \'Donald Duck\'],
[4, \'DBC\', 1, \'Michael Jackson\'],
[5, \'XYZ\', 2, \'Michael Jackson\'],
[6, \'BBC\', 4, \'Michael Jackson\'],
];
// owner_name subarrray index = 3
// publication_name subarrray index = 1
t.sort( (a,b)=> a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]) );
console.log(t.join(\'\\n\'));
I assume that your data in array let t = [ [publicationID, publication_name, ownderID, owner_name ], ... ]
where index of owner_name = 3 and publication_name =1.