Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I'm looking to sort an array of about 200-300 objects, sorting on a specific key and a given order (asc/desc). The order of results must be consistent and stable.

What would be the best algorithm to use, and could you provide an example of it's implementation in javascript?

Thanks!

share|improve this question
2  
Since at least Chrome's array sort doesn't seem to be stable, relying on the built-in array sort is not an option for you. – Nosredna Sep 15 '09 at 14:56
To summarize: I went with a hand rolled merge sort due to Array.sort stability inconsistencies between modern browsers (mainly chrome not implementing a stable sort at the time of this comment). Thanks everyone for your help! – Bill Casarin Sep 15 '09 at 15:30

7 Answers

up vote 18 down vote accepted

Since you are looking for something stable, the merge sort should do.

http://en.literateprograms.org/Merge_sort_(JavaScript)

EDIT:

According to this post, it looks like Array.Sort in some implementations uses a merge sort.

share|improve this answer
++ Merge sort is my favorite. It's simple and stable with no bad worst cases. – Mike Dunlavey Sep 15 '09 at 15:05
I'll look into this. Thanks! – Bill Casarin Sep 15 '09 at 15:09

Will the built in sort function do? http://www.w3schools.com/jsref/jsref%5Fsort.asp

share|improve this answer
1  
I don't think the built-in sort uses a stable algorithm – Philippe Leybaert Sep 15 '09 at 14:45
I was thinking the same thing. – Kevin Sep 15 '09 at 14:46
Oh, bummer. I second the suggested merge sort then :) – finalman Sep 15 '09 at 14:48
1  
Good point. It's not guaranteed, but browsers have moved toward using stable implementations. IE 6+ is stable. Firefox 3 is. Safari is. Not sure about Chrome (I'd hope it is). Or Opera (it used to be unstable--I hope it's fixed now). – Nosredna Sep 15 '09 at 14:50
Chrome not stable. Does anyone know if Opera 10 is? – Nosredna Sep 15 '09 at 14:57
show 1 more comment

Check this article:

http://www.hedgerwow.com/360/dhtml/js%5Farray%5Fstable%5Fsort.html

It seems that only FF 2.0 and Opera 9 and earlier have a non-stable sort implementation. All other mainstream browsers do.

share|improve this answer
3  
Yikes. I just tried Chrome and it doesn't look stable, so I'd say assuming stable in modern browsers is a big mistake. – Nosredna Sep 15 '09 at 14:54
That's why I was cautious and said "it seems" :-) Anyhow, I wouldn't rely on it. – Philippe Leybaert Sep 15 '09 at 14:58

It is possible to get a stable sorting from a non-stable sort function.

Before sorting you get the position of all the elements. In your sort condition, if both elements are equal, then you sort by the position.

Tada! You've got a stable sort.

I've written an article about it on my blog if you want to know more about this technique and how to implement it: http://blog.vjeux.com/2010/javascript/javascript-sorting-table.html

share|improve this answer

Counting Sort is faster than merge sort (it performs in O(n) time) and it is intended for use on integers.

Math.counting_sort = function (m) {
    var i
    var j
    var k
    var step
    var start
    var Output
    var hash
    k = m.length
    Output = new Array ()
    hash = new Array ()
    // start at lowest possible value of m
    start = 0
    step = 1
    // hash all values
    i = 0
    while ( i < k ) {
        var _m = m[i]
        hash [_m] = _m
        i = i + 1
    }
    i = 0
    j = start
    // find all elements within x
    while ( i < k ) {
        while ( j != hash[j] ) {
            j = j + step
        }
        Output [i] = j
        i = i + 1
        j = j + step
    }
    return Output
}

Example:

var uArray = new Array ()<br/>
var sArray = new Array ()<br/><br/>
uArray = [ 10,1,9,2,8,3,7,4,6,5 ]<br/>
sArray = Math.counting_sort ( uArray ) // returns a sorted array
share|improve this answer

I know that this question has been answered for some time, but I happen to have a good stable merge sort implementation for Array and jQuery in my clipboard, so I'll share it in the hopes that some future searchers might find it useful.

It allows you to specify your own comparison function just like the normal Array.sort implementation.

Implementation

// Add stable merge sort to Array and jQuery prototypes
// Note: We wrap it in a closure so it doesn't pollute the global
//       namespace, but we don't put it in $(document).ready, since it's
//       not dependent on the DOM
(function() {

  // expose to Array and jQuery
  Array.prototype.msort = jQuery.fn.msort = msort;

  function msort(compare) {

    var length = this.length,
        middle = Math.floor(length / 2);

    if (!compare) {
      compare = function(left, right) {
        if (left < right) 
          return -1;
        if (left == right)
          return 0;
        else
          return 1;
      };
    }

    if (length < 2)
      return this;

    return merge(
      this.slice(0, middle).msort(compare),
      this.slice(middle, length).msort(compare),
      compare
    );
  }

  function merge(left, right, compare) {

    var result = [];

    while (left.length > 0 || right.length > 0) {
      if (left.length > 0 && right.length > 0) {
        if (compare(left[0], right[0]) <= 0) {
          result.push(left[0]);
          left = left.slice(1);
        }
        else {
          result.push(right[0]);
          right = right.slice(1);
        }
      }
      else if (left.length > 0) {
        result.push(left[0]);
        left = left.slice(1);
      }
      else if (right.length > 0) {
        result.push(right[0]);
        right = right.slice(1);
      }
    }
    return result;
  }
})();

Example Usage

var sorted = [
  'Finger',
  'Sandwich',
  'sandwich',
  '5 pork rinds',
  'a guy named Steve',
  'some noodles',
  'mops and brooms',
  'Potato Chip Brand® chips'
].msort(function(left, right) {
  lval = left.toLowerCase();
  rval = right.toLowerCase();

  console.log(lval, rval);
  if (lval < rval)
    return -1;
  else if (lval == rval)
    return 0;
  else
    return 1;
});

sorted == ["5 pork rinds", "a guy named Steve", "Finger", "mops and brooms", "Potato Chip Brand® chips", "Sandwich", "sandwich", "some noodles"];
share|improve this answer

A simple one mergeSort from http://www.stoimen.com/blog/2010/07/02/friday-algorithms-javascript-merge-sort/

var a = [34, 203, 3, 746, 200, 984, 198, 764, 9];

function mergeSort(arr)
{
    if (arr.length < 2)
         return arr;

    var middle = parseInt(arr.length / 2);
    var left   = arr.slice(0, middle);
    var right  = arr.slice(middle, arr.length);

    return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right)
{
     var result = [];

    while (left.length && right.length) {
         if (left[0] <= right[0]) {
             result.push(left.shift());
         } else {
            result.push(right.shift());
         }
    }

    while (left.length)
        result.push(left.shift());

    while (right.length)
        result.push(right.shift());

    return result;
}

console.log(mergeSort(a));
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.