8

It seems like Array.prototype.sort() is broken with BigInt

This works

const big = [1n, 2n, 3n, 4n];
big.sort();
console.log(big);
// expected output: Array [1n, 2n, 3n, 4n]

But this doesn't :(

const big = [1n, 2n, 3n, 4n];
big.sort((a,b)=>a-b);
console.log(big);
//Error: Cannot convert a BigInt value to a number

or am i doing something wrong?

Laukik
  • 424
  • 4
  • 13
  • Does this answer your question? [How to sort an array of integers correctly](https://stackoverflow.com/questions/1063007/how-to-sort-an-array-of-integers-correctly) – Nishant Dec 24 '20 at 07:45
  • I think you mis read the question, it seems compare function only expects integer values in return.. – Laukik Dec 24 '20 at 07:47
  • I believe `BigInt` is handled differently from typical numerical values. You may need to use a separate library that makes working with these types of values much easier to properly subtract the two. I haven't worked with `BigInt` before, but from my limited understanding, that might be the root of the problem. – Sal Dec 24 '20 at 07:48
  • You could do `Number(a-b)` but not sure if it's the correct way to handle it. It converts the BigInt to number. If the difference is more than `Number.MAX_SAFE_INTEGER` (2^53 - 1), it will not work – adiga Dec 24 '20 at 07:49
  • 1
    Found this on Mozilla's website, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Comparisons (in the 3rd code block). It shows that sorting the way you have it is not supported, and offers a way to do what you are looking for. – Sal Dec 24 '20 at 07:52
  • found the answer below its working, the illusion is with -1n 0n 1n are within range of integer so compare fn expects it to be in integer and not bigint – Laukik Dec 24 '20 at 07:57

3 Answers3

12

JavaScript sort method requires a function as a parameter that can compare two elements of the array and return either a positive number, or a negative number or zero. Number is the keyword here.

BigInt operations like addition and subtraction returns BigInt type and not a Number type. And that's why the error you are getting.

So, Something like this should do the job

const big = [1n, 2n, 3n, 4n];
big.sort((a ,b) => {
  if(a > b) {
    return 1;
  } else if (a < b){
    return -1;
  } else {
    return 0;
  }
});
console.log(big);

Interestingly, MDN document that I linked to previously, also suggests how to sort an array of BigInts, and it is concise:

Copying the whole section here for posterity:

const mixed = [4n, 6, -12n, 10, 4, 0, 0n]
// ↪  [4n, 6, -12n, 10, 4, 0, 0n]

mixed.sort() // default sorting behavior
// ↪  [ -12n, 0, 0n, 10, 4n, 4, 6 ]

mixed.sort((a, b) => a - b)
// won't work since subtraction will not work with mixed types
// TypeError: can't convert BigInt to number

// sort with an appropriate numeric comparator
mixed.sort((a, b) => (a < b) ? -1 : ((a > b) ? 1 : 0))
// ↪  [ -12n, 0, 0n, 4n, 4, 6, 10 ]
Nishant
  • 54,584
  • 13
  • 112
  • 127
  • 2
    You also need to return 0 when `a === b` – adiga Dec 24 '20 at 07:52
  • I *think* that should not matter because what difference would it make it the sort method puts one `2n` before another `2n` or after. Updated the answer, as document says sort should return positive, zero, or negative number. – Nishant Dec 24 '20 at 07:57
  • 1
    [Sorting in JavaScript: Should every compare function have a “return 0” statement?](https://stackoverflow.com/questions/20883421) – adiga Dec 24 '20 at 08:01
1

The reason is that a - b in the sort callback function will return a BigInt data type, while sort expects it to return something that is (or can coerce to) a Number data type.

So you can use a > b || -(a < b) as callback expression:

const big = [10n, 9n, 8n, 7n];
big.sort((a, b) => a > b || -(a < b));
console.log(big + ""); // 7,8,9,10

Note that the first version (without sort callback) does not work in general, because then sort will compare the elements as strings. It is clear that this can yield results that are not numerically sorted:

const big = [10n, 9n, 8n, 7n];
big.sort(); // string-based sort
console.log(big + ""); // 10,7,8,9 is wrong
trincot
  • 317,000
  • 35
  • 244
  • 286
0

This might appear to be broken at first but its not, the problem is definition of the compare function is always expecting a return value of -1, 0, 1 and it has no reason to expect bigint of -1 0 1 cause they all are in range of normal int.. so this should solve

big.sort((a,b)=>a<b?-1:(a>b?1:0));
Laukik
  • 424
  • 4
  • 13