views:

459

answers:

4

Hi,

i found the following solution to add sorting capabilities to jQuery (ref: jQuery sort()). I've altered the solution to sort strings of unknown length. It works nicely, however as you might notice from the function name: it sorts acscending :-).

jQuery.fn.sort = function() {  
    return this.pushStack( [].sort.apply( this, arguments ), []);  
};  

function sortStringAscending(a,b){
    var h1 = a.innerHTML;
    var h2 = b.innerHTML;
    var c = 0;
    var str1 = null,
    var str2 = null;

    while(str1 == str2 && (c < h1.length && c < h2.length)) {
        str1 = h1.substring(c,c+1).toLowerCase().charCodeAt(0);
        str2 = h2.substring(c,c+1).toLowerCase().charCodeAt(0);

        if(str1 > str2) {
            r = 1;
        } else if(str2 > str1) {
            r = -1;
        }

        c += 1;
    }
    return r;  
};

The function is used like this:

$('ol li').sort(sortStringAscending).appendTo('ol');

Is it possible to alter this code in such a way that the following will become possible?

$('ol li').sort(sortString, 0).appendTo('ol');  //0 for descending
$('ol li').sort(sortString, 1).appendTo('ol');  //1 for ascending
A: 

Changing this:

if(str1 > str2) {
    r = 1;
} else if(str2 > str1) {
    r = -1;
}

to this:

if(str1 > str2) {
    r = -1;
} else if(str2 > str1) {
    r = 1;
}

will swap the sort order.


EDIT:

Because of the way Javascript's array.sort(callback) function work it is not easy to just add a third parameter for the callback to accept, I will suggest using 2 separate functions mySortAsc and mySortDesc which in turn could be calling a third function and passing whatever parameters are needed.

duckyflip
Note: He wants to pass a parameter.
altCognito
Yes i want to pass a parameter :-)
Ropstah
Mario was first :-)
Ropstah
A: 

This will do what you've asked: (use a parameter to decide sort order)

function sortString(a,b,direction){
var d = (direction?1:-1);
var h1 = a.html();
var h2 = b.html();
var c = 0;
var str1 = null,
var str2 = null;

while(str1 == str2 && (c < h1.length && c < h2.length)) {
    str1 = h1.substring(c,c+1).toLowerCase().charCodeAt(0);
    str2 = h2.substring(c,c+1).toLowerCase().charCodeAt(0);

    if(str1 > str2) {
        r = 1*d;
    } else if(str2 > str1) {
        r = -1*d;
    }

    c += 1;
}
return r;

};

altCognito
No this doesn't work, I tried this one myself. The thing is I'm passing the function delegate (sortString) to the sort method:$('ol li').sort(sortString).appendTo('ol'); Adding arguments to the sort() method, does NOT add these arguments to the delegate function sortString...
Ropstah
altCognito, Array.prototype.sort() takes one parameter which it expects to be a function callback, it then passess 2 parameters to that callback which are the curr/next elements in the array. Therefore you can not just add a third parameter to the callback as there's no way to actually pass it on
duckyflip
Doh, yeah, totally missed that he had defined it that way. (note, that it was defined)
altCognito
@duckyflip I'm feeling kind lazy, but actually, you could redefine the sort function, I don't feel like spelling this out right now, but yeah, I've got it wrong there as well.
altCognito
+3  A: 

You are not going to be able to easily get an additional argument into the array sort function that the jQuery.fn.sort uses.

It will be easier to use two separate functions, one for ascending, and one for descending, but keep the actual comparision in a third function.

function sortStringAscending(a,b) {
    return sortString(a,b,1);
};
function sortStringDescending(a,b) {
    return sortString(a,b,-1);
};
function sortString(a,b,direction) {
   var h1 = a.innerHTML.toLowerCase();
   var h2 = b.innerHTML.toLowerCase();

   if(h1 > h2) {
      r = 1*direction;
   } else if(h2 > h1) {
      r = -1*direction;
   }
   return r;  
};

Also note that you can simply compare two strings, there is no need to compare them character by character, unless you want to have unpredictable sort order for items like "abc" and "abcd".

Then you should be able to do

$('ol li').sort(sortStringAscending).appendTo('ol');
$('ol li').sort(sortStringDescending).appendTo('ol');
Mario Menger
Thought it wasn't possible. This would be my solution anyway ;)
Ropstah
A: 

You might be able to use a closure. Although the javascript array sort method expects a 2 parameter function, you can bring other information into the callback function's scope by doing something like:

function BiSort(direction)
{
    return  function( a, b )
            {
                var d = ( direction ? 1 : -1 ) ;

                return ( a > b ? 1 : b > a ? -1 : 0 ) * d ; 
            }
}

The BiSort function returns a new function that has access to the outer function's parameter list. Now you can do this:

var a = ["ant", "goat", "bat", "zebra", "yak"] ;

a.sort(BiSort(true));  // sorts ascending

a.sort(BiSort(false)); // sort descending