I have a bunch of songs stored in an array of JavaScript objects. It looks like this:
var library = [
{ "title": "40 Years Later", "album": "Tears of Steel", "year": 2012, "track": 1, "disk": 1, "time": 31 },
{ "title": "The Dome", "album": "Tears of Steel", "year": 2012, "track": 2, "disk": 1, "time": 311 },
{ "title": "The Battle", "album": "Tears of Steel", "year": 2012, "track": 3, "disk": 1, "time": 123 },
{ "title": "End Credits", "album": "Tears of Steel", "year": 2012, "track": 4, "disk": 1, "time": 103 },
{ "title": "The Wires", "album": "Elephants Dream", "year": 2006, "track": 1, "disk": 1, "time": 75 },
{ "title": "Typewriter Dance", "album": "Elephants Dream", "year": 2006, "track": 2, "disk": 1, "time": 70 },
{ "title": "The Safest Place", "album": "Elephants Dream", "year": 2006, "track": 3, "disk": 1, "time": 45 },
{ "title": "Emo Creates", "album": "Elephants Dream", "year": 2006, "track": 4, "disk": 1, "time": 60 },
{ "title": "End Title", "album": "Elephants Dream", "year": 2006, "track": 5, "disk": 1, "time": 91 },
{ "title": "Teaser Music", "album": "Elephants Dream", "year": 2006, "track": 6, "disk": 1, "time": 75 },
{ "title": "Ambience", "album": "Elephants Dream", "year": 2006, "track": 7, "disk": 1, "time": 110 },
{ "title": "Snow Fight", "album": "Sintel", "year": 2010, "track": 1, "disk": 1, "time": 107 },
{ "title": "Finding Scales / Chicken Run", "album": "Sintel", "year": 2010, "track": 2, "disk": 1, "time": 107 },
{ "title": "The Ziggurat", "album": "Sintel", "year": 2010, "track": 3, "disk": 1, "time": 78 },
{ "title": "Expedition", "album": "Sintel", "year": 2010, "track": 4, "disk": 1, "time": 93 },
{ "title": "Dragon Blood Tree", "album": "Sintel", "year": 2010, "track": 5, "disk": 1, "time": 47 },
{ "title": "Cave Fight / Lament", "album": "Sintel", "year": 2010, "track": 6, "disk": 1, "time": 145 },
{ "title": "I Move On (Sintel's Song)", "album": "Sintel", "year": 2010, "track": 7, "disk": 1, "time": 169 },
{ "title": "Circling Dragons", "album": "Sintel", "year": 2010, "track": 8, "disk": 1, "time": 28 },
{ "title": "Trailer Music", "album": "Sintel", "year": 2010, "track": 9, "disk": 1, "time": 44 }
];
I need to sort by properties in both alphabetical and numerical order. There are lots of articles and questions on StackOverflow that cover sorting by a single value, and some that seem to cover multiple values but they have no order or importance.
I need to sort each object (song) by several values where each value is of a lower importance rank. For example:
album
name (alphabetical) >disk
number (numerical) >track
number (numerical) >title
(alphabetical) > etc.
This means that albums are together where each album is in alphabetical order. In each album are songs sorted by the disk number, so all songs in disk 1 are at the top, then all songs in disk 2, etc. Inside each grouping of disk numbers are songs sorted by track number. If there are multiple songs with the same track number, or no track number exists, they will be sorted by song title in alphabetical order. More properties can be given if track title is also the same.
Capitalization should not matter, and it should sort like normal alphabetical sorting would (if it started with a number, that would go before letters, and special characters would have their place like in traditional sorting).
I have tried using this code (which has hard-coded sort values and lacks disk numbers) but it is only sorting by the inner-most property in the code, track number.
library.sort(function (a, b) {
if (a.album === b.album) {
if (a.track === b.track) {
var x = a.title.toLowerCase();
var y = b.title.toLowerCase();
return x < y ? -1 : x > y ? 1 : 0;
}
var x = a.track;
var y = b.track;
return x < y ? -1 : x > y ? 1 : 0;
}
return a.album.toLowerCase() - b.album.toLowerCase();
});
I am looking for a function that will rearrange the library
array so that it is sorted as described above given something along the lines of the following input:
sort(library, [ "album", "disk", "track", "title" ]);
Individual values should also be able to be sorted in a descending order, maybe looking something like -album
or ["album", true]
. Syntax is flexible.
You can sort array of ojects with Alasql JavaScript SQL library. It supports sorting with many fields in any order (like SQL).
For many sorts Alasql may be faster than other sort functions, because it compiles queries to JavaScript and save these compiled query function to cache.
var res = alasql('SELECT *, LCASE(album) AS lalbum, LCASE(title) as ltitle FROM ? \
ORDER BY lalbum DESC, disk, ltrack, ltitle',[library]);
Here I created two additional fields (lalbum and ltitle) for sort text in case-insensitive way.
Try this example with you data at jsFiddle
You could use a function like this:
function priority(opt) {
if (!(opt instanceof Array)) {
opt = Array.prototype.slice.call(arguments);
}
return function (a, b) {
for (var i = 0; i < opt.length; ++i) {
var option = opt[i];
if (typeof option === 'string') {
option = [option, '+'];
}
if (option.length < 2) {
option[1] = '+';
}
if (a[option[0]] !== b[option[0]]) {
if (a[option[0]] === undefined) return 1;
if (b[option[0]] === undefined) return -1;
if (typeof a[option[0]] === 'string' || typeof b[option[0]] === 'string') {
return (option[1] === '+' ? String(a[option[0]]).toLowerCase() < String(b[option[0]]).toLowerCase() : String(a[option[0]]).toLowerCase() > String(b[option[0]]).toLowerCase()) ? -1 : 1;
} else {
return (option[1] === '+' ? a[option[0]] < b[option[0]] : a[option[0]] > b[option[0]]) ? -1 : 1;
}
}
}
return 0;
};
}
it works like this:
library.sort(priority(['album', 'disk', ['title', '-']])
this will sort library by album ascending, then disk ascending, then title descending
Formal usage:
opt: array containing:
or
I didn't implement it the exact way you said, because it would make keys beginning with -
impossible to sort in ascending order.
EDIT:
alternate version, works with '-album'
syntax:
function priority(opt) {
if (!(opt instanceof Array)) {
opt = Array.prototype.slice.call(arguments);
}
return function (a, b) {
for (var i = 0; i < opt.length; ++i) {
var order = opt[i].substr(0, 1),
key = opt[i].substr(1);
if (order !== '-' && order !== '+') {
key = opt[i];
order = '+';
}
if (a[key] !== b[key]) {
if (a[key] === undefined) return 1;
if (b[key] === undefined) return -1;
if (typeof a[key] === 'string' || typeof b[key] === 'string') {
return (order === '+' ? String(a[key]).toLowerCase() < String(b[key]).toLowerCase() : String(a[key]).toLowerCase() > String(b[key]).toLowerCase()) ? -1 : 1;
} else {
return (order === '+' ? a[key] < b[key] : a[key] > b[key]) ? -1 : 1;
}
}
}
return 0;
};
}
to be used like this:
library.sort(priority(['album', '-year', '+title']));
or
library.sort(priority('album', '-year', '+title'));
Here you go.. one approach to the problem:
// This is kinda hacky, but it works
// "a" means do an alphabetical compare
// "n" means do a numeric compare.
var sortKeys = ["album", "a", "disk", "n", "track", "n", "title", "n"];
function byKey(ao, bo) {
var l = sortKeys.length;
var i = 0;
var sortResult;
// Walk through the keys
while (i < l) {
// Get the field name
var field = sortKeys[i];
// Get the compare type
var sortType = sortKeys[i + 1];
// Get the values and force to string values
var a = "" + ao[field];
var b = "" + bo[field];
console.log([field, sortType]);
// Advance by two because we consume two array elements
i += 2;
// Our alphabletical compare
if (sortType === "a") {
if (a.toLowerCase() < b.toLowerCase()) {
return -1;
}
if (a.toLowerCase() > b.toLowerCase()) {
return +1;
}
if (a.toLowerCase() === b.toLowerCase()) {
// Ok, these fields match. Restart the loop
// So it will try the next sort criteria in.
continue;
}
throw ("Should never actually get here.");
}
if (sortType === "n") {
// Cheap numeric compare
return +a - +b;
}
}
// A total match across all fields
return 0;
}
library.sort(byKey);
console.log(JSON.stringify(library, null, 2));
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.