This is a bit trickier that it may seem at first. But here is one way to do it, assuming the special characters listed are the only ones you're concerned with:
var separator = '\uFFFF';
var prefixes = {
'~': '1',
'&': '2',
'@': '3',
'%': '4',
'+': '5'
};
function specialsort( array ) {
var prefixed = array.map( function( value ) {
var prefix = prefixes[ value.charAt(0) ] || '9';
return prefix +
value.toLowerCase() +
separator + value;
});
prefixed.sort();
return prefixed.map( function( value ) {
return value.split(separator)[1];
});
}
var nicks = [ "~xxx", "@blue", "&demo", "+voice", "%yyy", "nick1", "Nick2", "webmaster" ];
var sorted = specialsort( nicks );
console.log( sorted );
The code works by creating a new array with strings formatted like this:
- The first character is a digit 1-5 for the special characters or 9 for any other character found at the beginning of each string. (You could extend this to two digits using '01', '02', etc. and '99' for other characters.)
- Next is the string in lowercase.
- Then a Unicode character with a very large value (
\uFFFF) as a separator.
- Finally, the original string.
The array of these strings can then be sorted directly, and the result is converted to a new array by splitting the strings on that separator and using the part after the separator (the original string).
Or, a slightly simpler approach using a sort callback function:
var prefixes = {
'~': '1',
'&': '2',
'@': '3',
'%': '4',
'+': '5'
};
function specialsort( array ) {
return array.sort( function( a, b ) {
a = ( prefixes[ a.charAt(0) ] || '9' ) + a.toLowerCase();
b = ( prefixes[ b.charAt(0) ] || '9' ) + b.toLowerCase();
return a < b ? -1 : a > b ? 1 : 0;
});
}
var nicks = [ "~xxx", "@blue", "&demo", "+voice", "%yyy", "nick1", "Nick2", "webmaster" ];
var sorted = specialsort( nicks );
console.log( sorted );
For lengthy arrays, I tend to go for the first approach—making a modified array and sorting it—because it can be faster than using a sort callback. But the sort callback is a bit simpler and there's nothing wrong with it for an array of this size.
The sort callback approach does have one other advantage over the modified array: it doesn't depend on that slightly hacky separator character.
Either way, the output is:
["~xxx", "&demo", "@blue", "%yyy", "+voice", "nick1", "Nick2", "webmaster"]
Here's a fiddle for the first version and a fiddle for the second version.