16

I'm trying to sort an array that sometimes has Infinity or NaN. When I use a standard JavaScript array.sort() it seems to sort until it reaches a NaN and then I get random results after that.

var array =[.02,.2,-.2,Nan,Infinity,20];

Is there a way to still sort this so that the end result is from negative to positive and still have NaN or Infinity at the end.

-.2,.02,.2,20,NaN,Infinity
Yves M.
  • 29,855
  • 23
  • 108
  • 144
techdog
  • 1,451
  • 3
  • 19
  • 38
  • Can you give us an example of the array? – Xotic750 Jul 09 '13 at 20:42
  • 2
    Will adjusting the logic in the function to deal with the NaN special case work? Some sort of if(isNaN(a)) { return positive } // so it's always at one end – loosebazooka Jul 09 '13 at 20:47
  • 1
    @loosebazooka 's comment is the answer to the question – Francisco Meza Jul 09 '13 at 20:49
  • Oops, I forgot a number ordering... fixed the answer. – Teemu Jul 09 '13 at 21:37
  • @loosebazooka, but for the sake of sorting, NaN should equal NaN (even though `NaN !== NaN` in JS) – Brigand Jul 09 '13 at 21:44
  • @daniel savage, I would say it is because it is unclear what you are expecting as a result, your question is vague and not well defined. – Xotic750 Jul 09 '13 at 22:01
  • @daniel savage, now that you have clarified a little more, this renders my answer incorrect (and some others too) and I will be removing it. It is still unknown whether you will ever have `-Infinity`, and if yes, in what order it should appear. – Xotic750 Jul 09 '13 at 22:23
  • Upvoting this back to zero, since this question is now sufficiently clear. (What's the point in downvoting a question if you don't upvote again when the problem is fixed?) @HoboSapiens: Trying to invalidate the question is poor form. Just because an operation is mathematically undefined doesn't mean it has to be algorithmically undefined. Languages and libraries designed more thoughtfully than Javascript, such as NumPy, handle this issue with grace; the fact that Javascript doesn't is a design bug, IMHO. – Michael Scheper Jul 21 '15 at 18:19

5 Answers5

14

If you just want to bump them to the end in a random order:

var arr = [-1, 0, 1, 10, NaN, 2, NaN, 0, -1, NaN, 5, Infinity, 0, -Infinity];

arr.sort(function(a,b){
    if( !isFinite(a) && !isFinite(b) ) {
        return 0;
    }
    if( !isFinite(a) ) {
        return 1;
    }
    if( !isFinite(b) ) {
        return -1;
    }
    return a-b;
});
//[-1, -1, 0, 0, 0, 1, 2, 5, 10, NaN, NaN, NaN, Infinity, -Infinity]

If you want to also sort the infinities at the end:

var arr = [-1, 0, 1, 10, NaN, 2, NaN, 0, -1, NaN, 5, Infinity, 0, -Infinity];

arr.sort(function(a,b){
    if( !isFinite(a) && !isFinite(b) ) {
        return ( isNaN(a) && isNaN(b) )
            ? 1
            : a < b
                ? -1
                : a === b
                    ? 0
                    : 1;
    }
    if( !isFinite(a) ) {
        return 1;
    }
    if( !isFinite(b) ) {
        return -1;
    }
    return a-b;
});

//[-1, -1, 0, 0, 0, 1, 2, 5, 10, -Infinity, Infinity, NaN, NaN, NaN]

Here the order is -Infinity < Infinity < NaN

Esailija
  • 138,174
  • 23
  • 272
  • 326
13

Negative infinity should logically be ordered first, as it is effectively smaller than all other numbers.

I would thus do it like this:

const cmp = (a,b) => a-b || isNaN(a)-isNaN(b);

// Example
const arr = [Infinity, NaN, Infinity, -Infinity, NaN, 1, 0, NaN, -1, -0];
console.log(arr.sort(cmp));

Here -0 and 0 are considered equal and can appear in any relative order.

If you want to ensure that -0 gets ordered before 0, then:

const cmp = (a,b) => a-b || isNaN(a)-isNaN(b) || Object.is(b, -0) - Object.is(a, -0);

// Example
const arr = [Infinity, NaN, Infinity, -Infinity, NaN, 1, 0, NaN, -1, -0];
console.log(arr.sort(cmp));

Note that Stack Snippets does not retain the -0... See browser console.

trincot
  • 317,000
  • 35
  • 244
  • 286
  • 2
    This is the best modern approach answer which also fixes a chrome issue I was having with Fabian's answer. – ekfuhrmann Sep 26 '19 at 15:09
  • This answer includes a `-0` in its test case, and yet the test case fails on Firefox 113. `[ -Infinity, -1, 0, -0, 1, Infinity, Infinity, NaN, NaN, NaN ]` – Stefnotch May 20 '23 at 19:46
  • @Stefnotch, are you suggesting that -0 < 0? – trincot May 20 '23 at 19:49
  • I am indeed suggesting that if one defines a total order for doubles, then `-0` comes before `0`. Of course the question didn't ask for it, so I suppose it doesn't matter for the sake of this answer. – Stefnotch May 20 '23 at 19:53
  • Alright, added an expanded version to my answer for enforcing that order. – trincot May 20 '23 at 19:59
12

You can catch NaN and Infinity using JavaScript's built-in utility functions for those cases:

let array = [Infinity, -1, 6, 1, 0, NaN, 0, -1, 2, 5, 10, -Infinity, NaN, Infinity, NaN]



//sort -Infinity, NaN, Infinity to the end in random order
array.sort(function(a,b){
  if(isFinite(a-b)) {
    return a-b; 
  } else {
    return isFinite(a) ? -1 : 1;
  }
});

//[-1,-1,0,0,1,2,5,6,10,NaN,Infinity,Infinity,NaN,-Infinity,NaN]
console.log(array);



//sort -Infinity<0<Infinity<NaN
array.sort(function(a,b){
  if(isNaN(a)) { 
    return 1-isNaN(b);
  } else {
    return a-b; 
  }
});

//[-Infinity,-1,-1,0,0,1,2,5,6,10,Infinity,Infinity,NaN,NaN,NaN]
console.log(array);
Fabian N.
  • 3,807
  • 2
  • 23
  • 46
  • Parenthesis at the end is missing. Seems to work now for my test case. – techdog Jul 09 '13 at 21:43
  • 2
    It is still essentially shuffling the array. Also, `isFinite` already checks for `NaN` – Esailija Jul 09 '13 at 21:46
  • It's hard to comprehend how this can get accepted as an answer when it is clearly broken. [jsfiddle](http://jsfiddle.net/Xotic750/T6Lj8/) -> `[2, NaN, -1, 0, 0, 0, 1, -1, 5, 10, -Infinity, NaN, NaN, Infinity]` – Xotic750 Jul 09 '13 at 22:21
  • Your jsfiddle http://jsfiddle.net/T6Lj8/1 gives as result `-1,-1,0,0,0,1,2,5,10,NaN,NaN,NaN,Infinity,-Infinity` Where is the problem ? (Tested with Firefox and Opera) ah and fails with Chrome ... -.- – Fabian N. Jul 09 '13 at 22:49
  • In both Chromium v25 and Google Chrome v28 it shows `[2, NaN, -1, 0, 0, 0, 1, -1, 5, 10, -Infinity, NaN, NaN, Infinity] ` – Xotic750 Jul 09 '13 at 22:52
  • array.sort() working differently in chrome and firefox and ie http://stackoverflow.com/questions/11832158/array-sort-working-differently-in-chrome-and-firefox-and-ie ... I fixed the problem (http://jsfiddle.net/T6Lj8/2/ Tested with Firefox, Opera, Chrom) – Fabian N. Jul 09 '13 at 22:59
  • Chromium uses an unstable sort (not sure which method) whereas FireFox uses a [stable sort](http://en.wikipedia.org/wiki/Sorting_algorithm#Stability) (a merge-sort I believe). [ECMA-262](http://ecma-international.org/ecma-262/5.1/#sec-15.4.4.11) does not specify which type of sort must be used and rather leaves it up to the vendor to choose. – Xotic750 Jul 09 '13 at 23:07
  • 1
    @Xotic750 To make it more confusing V8/Chrome/Chromium uses Insertion Sort (Stable) for arrays with <= 22 items and Quicksort (Unstable) for arrays with more items :P – Esailija Jul 10 '13 at 17:32
  • Cleaned up the code of the answer, tested to be still working in Chrome and Firefox – Fabian N. Jul 18 '18 at 10:33
  • Still broken, in Microsoft Edge Version 113, the array `[NaN,0]` gets sorted as `[NaN,0]`. Meanwhile in Firefox Version 113, it gets sorted as `[0, NaN]`. The second compare function simply doesn't handle the case where `b` is NaN. – Stefnotch May 20 '23 at 19:44
0

Something like this?

var arr = [-1, 0, 1, 10, NaN, 2, NaN, 0, -1, NaN, 5, Infinity, 0];
function sortInf (a, b) {
    a = parseFloat(a);
    b = parseFloat(b);
    if ((!a || a === -Infinity) && a !== 0) {
        return 1;
    } else if ((!b || b === -Infinity) && b !== 0) {
        return -1;
    } else return a - b;
}
alert(arr.sort(sortInf));
Teemu
  • 22,918
  • 7
  • 53
  • 106
  • 1
    Doesn't work with `[-1, 0, 1, 10, NaN, 2, NaN, 0, -1, NaN, 5, Infinity, 0, -Infinity]` – Esailija Jul 09 '13 at 21:11
  • @Esailija Thanks for the info, I just didn't test it with all possible combinations, fixed the code... – Teemu Jul 09 '13 at 21:18
  • 1
    Still doesn't appear sorted to me [jsfiddl](http://jsfiddle.net/Xotic750/aL2Fy/) -> `[-1, -1, 0, 0, 0, 1, 2, 5, 10, Infinity, NaN, -Infinity, NaN, NaN]` – Xotic750 Jul 09 '13 at 22:28
  • @Xotic750 OP says "`NaN or Infinities at the end`", I understood the order of the Infinities and NaNs doesn't matter, just they appear at the end of the array. Please correct me if I'm wrong with this... – Teemu Jul 09 '13 at 22:37
  • It could certainly be taken that way as the OP is unclear, though `sort` for me means of a particular ordering and I wouldn't expect to see `Infinity, NaN, -Infinity, NaN`. Though the latest update from the OP shows `-.2,.02,.2,20,NaN,Infinity` – Xotic750 Jul 09 '13 at 22:47
  • @Xotic750 Well, I can understand your point of the view, order is an order, even a looser one ; ). Looks like OP isn't online anymore, so asking him won't help at the moment... – Teemu Jul 09 '13 at 22:54
  • Such is the problem with poorly defined questions. I removed my answer after the last question update. – Xotic750 Jul 09 '13 at 23:10
-1

a simple and fast way without conditional or function overhead:

var r=[1,9,NaN,3,4,5,0,-4, NaN , 4, Infinity, 7, 2];
 r.sort(function(a,b,c){return  a-b || (a||Infinity)-(b||Infinity) || 0 });
 alert(r) // == -4,0,1,2,3,4,4,5,7,9,NaN,NaN,Infinity

EDIT: updated based on feedback to avoid a NaN return.

this executes about 20X faster than the other answers, so it's ideal if you need perf, and sorting is one area where perf often DOES matter...

dandavis
  • 16,370
  • 5
  • 40
  • 36
  • This returns `NaN`s to the sort implementation – Esailija Jul 09 '13 at 21:20
  • @Esailija: why is that bad? can you please elaborate? (it seems to work for me) – dandavis Jul 09 '13 at 21:21
  • The custom function should return less than 0, 0, or more than 0. Returning NaN doesn't make any sense. If it works for one input in one browser then good for you. All the specifications says for this that the results for incorrect sort functions are implementation defined. – Esailija Jul 09 '13 at 21:22
  • @Esailija: interesting. are there any browsers you know of where this causes a problem? – dandavis Jul 09 '13 at 21:25
  • 1
    What is happening here though? [jsfiddle](http://jsfiddle.net/Xotic750/9DcW8/) -> `-Infinity, -1, -1, 0, 0, 0, 1, 2, 5, 10, NaN, NaN, NaN, Infinity] ` – Xotic750 Jul 09 '13 at 21:55
  • @Esailija: ahh, the old "your answer doesn't solve a different question than posed" routine. I never saw anything about -Infinity. in fact, i've never see -Infinity in the wild, but if handling it is worth a 20X slowdown, then you gotta do what you gotta do. – dandavis Jul 09 '13 at 22:01
  • 2
    I don't see any backup for the 20X, for instance here it is actually slower in firefox http://jsperf.com/arrsarrs. If you need to sort things a lot as to worry about performance I would actually suggest using a different data structure like a binary search tree. – Esailija Jul 09 '13 at 22:03
  • @Esailija: the 20X i measured was on the data set in my answer. on the OP's data, your functions are only 2-3X slower in v8... – dandavis Jul 09 '13 at 22:12
  • 1
    Still doesn't work even with `-Infinity` removed, [jsfidde](http://jsfiddle.net/Xotic750/7kaYt/) - > `[-1, -1, NaN, 0, NaN, 0, 0, 1, 2, 5, 10, Infinity, NaN]` – Xotic750 Jul 09 '13 at 22:39