Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding sort() compareFunction

I'm working with an ecommerce platform that lacks the ability to reorder the options of our product attribute fields. It really sucks because to insert a new option you pretty much have to delete all of the existing ones and start over. I'm trying to do it client-side instead. Here's what I'm working with (this one's for a shoe size):

  • 9 EE
  • 9 1/2 EE
  • 10 EE
  • 10 1/2 EE
  • 11 EE
  • 11 1/2 EE
  • 9 EEEE
  • 9 1/2 D
  • 9 1/2 EEEE
  • 10 EEEE
  • 10 1/2 EEEE
  • 11 EEEE
  • 9 D
  • 11 1/2 EEEE

These are actually the text of some <option>s in a form. The format of the values is X Y Z where:

  • X is a whole number
  • Y is the string "1/2" and may not be present
  • Z is a letter code which is either "D", "E", "EEE", or "EEEE", and may not be present

The desired order of the above would be this:

  • 9 D
  • 9 1/2 D
  • 9 EE
  • 9 1/2 EE
  • 9 EEEE
  • 9 1/2 EEEE
  • 10 EE
  • 10 1/2 EE
  • 10 EEEE
  • 10 1/2 EEEE
  • 11 EE
  • 11 1/2 EE
  • 11 EEEE
  • 11 1/2 EEEE

I've learned a little bit about javascript's sort() function but haven't been able to fully comprehend how the comparison function that you can pass to it works. I've got this so far:

<select>
    <option>9 EE</option>
    <option>9 1/2 EE</option>
    <option>10 EE</option>
    <option>10 1/2 EE</option>
    <option>11 EE</option>
    <option>11 1/2 EE</option>
    <option>9 EEEE</option>
    <option>9 1/2 D</option>
    <option>9 1/2 EEEE</option>
    <option>10 EEEE</option>
    <option>10 1/2 EEEE</option>
    <option>11 EEEE</option>
    <option>9 D</option>
    <option>11 1/2 EEEE</option>
</select>

I started with the code taken from this answer: https://stackoverflow.com/a/667198/398242

$("select").html($("option").sort(function (a, b) {
    return a.text == b.text ? 0 : a.text < b.text ? -1 : 1
}));

Which sorts the items like this (doesn't work for even the first criteria):

  • 10 1/2 EE
  • 10 1/2 EEEE
  • 10 EE
  • 10 EEEE
  • 11 1/2 EE
  • 11 1/2 EEEE
  • 11 EE
  • 11 EEEE
  • 9 1/2 D
  • 9 1/2 EE
  • 9 1/2 EEEE
  • 9 D
  • 9 EE
  • 9 EEEE

I see that in javascript '11' > '9' returns false, which in no way makes sense to me.

MDN describes the compare function argument as such, and I kind of get it:

function compare(a, b) {
  if (a is less than b by some ordering criterion)
     return -1;
  if (a is greater than b by the ordering criterion)
     return 1;
  // a must be equal to b
  return 0;
}

...but I haven't got a clue how to adapt this to fit my requirements. I've tried a few things but I just feel like I'm shooting in the dark. I've tried to show that I've taken some time to attempt an understanding of this problem. I'm interested in learning more, but for now I'd just like to get this issue solved.

http://jsfiddle.net/DnwJ6/ Any clues?

like image 895
Wesley Murch Avatar asked Jul 10 '13 03:07

Wesley Murch


People also ask

What is the sort () method?

The sort() method sorts the elements of an array in place and returns the reference to the same array, now sorted. The default sort order is ascending, built upon converting the elements into strings, then comparing their sequences of UTF-16 code units values.

How does sort Work Ruby?

The Ruby sort method works by comparing elements of a collection using their <=> operator (more about that in a second), using the quicksort algorithm. You can also pass it an optional block if you want to do some custom sorting. The block receives two parameters for you to specify how they should be compared.

What happens when you sort an array?

sort takes an array and — you guessed it — sorts it in place. No copy of the array is created (as with map , etc.), so the array itself is altered with the sorted values. It can be used with or without a compareFunction , and when one isn't provided, it will automatically sort in ascending order.

How does C# sort work?

C# is using a default comparer method to sort integers numerically. The Sort method orders the integers in ascending order, while the Reverse method in descending order. The following example sorts integers with LINQ. In LINQ, we can choose between the query syntax or the method syntax.


2 Answers

Since you have formatted text, one way is to normalize the text elements before comparing them.

This solution may not be that optimal, but will do the job

$("select").html($("option").sort(function (a, b) {
    return nomalize(a.text) < nomalize(b.text) ? -1 : 1;
}));

function nomalize(val){
    var parts =  val.split(' '), op = '';

    op = parts[0].length == 1 ? '0' + parts[0] : parts[0];
    if(parts.length > 1){
        if(/[a-z]/i.test(parts[1])){
            op += '0/0' + parts[1];
        } else {
            op += parts[1]
        }
    }

    op += parts.length > 2 ? parts[2] : '';
    return op;
}

Demo: Fiddle

If somebody can suggest any solution to optimize it further it will be great

like image 100
Arun P Johny Avatar answered Oct 13 '22 16:10

Arun P Johny


Put the values in an array in the order you want them:

var shoeSizes = [ '9 D','9 1/2 D','9 EE','9 1/2 EE', ...]

Use Array.protoype.indexOf (with a shim for older browsers) to get the index of the matched text in the array. Use the index for the value to compare, something like:

function(a,b) {
  return shoeSizes.indexOf(a) - shoeSizes.indexOf(b);
}

If you need to deal with values that aren't in the array, test the value returned by indexOf and substitute a default if it's -1.

Alternatively you can make the sizes the names of property values in an object and assign a specific value:

var shoeSizes = { '9 D': 5, '9 1/2 D': 10, '9 EE': 15, '9 1/2 EE': 20, ...};

Then use the value in the compare:

function(a,b) {
  return shoeSizes[a] - shoeSizes[b];
}

Or to allow for default values:

function(a,b) {
  return (a in shoeSizes? shoeSizes[a] : 1000) - (b in shoeSizes? shoeSizes[b] : 1000);
}
like image 43
RobG Avatar answered Oct 13 '22 18:10

RobG