Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement Array.prototype.sort default compare function?

Background

I need to implement a function with the same behaviour as the default compare function of Array.prototype.sort

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

After reading the documentation I stumbled upon this:

The default sort order is according to string Unicode code points.

What does this mean? Does it mean I convert every object to a string?

If so, assuming I have the array [2, "a", { hello: "world" }] would these steps be correct?

  1. convert [2, "a", { hello: "world" }] to ["2", "a", '{ hello: "world" }']
  2. convert the first character of every string to a Number
  3. order by that number

Question

How do I implement a compare function that given any object, behaves exactly as the compare function from sort?

Notes

Reading the ECMA specification:

  • http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.sort

I now believe that if comparefn is undefined, then they use this algorithm as a default:

http://www.ecma-international.org/ecma-262/6.0/#sec-sortcompare

Can someone confirm?

like image 927
Flame_Phoenix Avatar asked Nov 16 '17 16:11

Flame_Phoenix


1 Answers

Solution

After reading the ECMA spec and asking around, I arrived to a defaultCompare function that simulates the default behaviour of Array.prototype.sort() in chrome:

const defaultCompare = ( x, y ) => {
    //INFO: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
    //ECMA specification: http://www.ecma-international.org/ecma-262/6.0/#sec-sortcompare

    if( x === undefined && y === undefined )
        return 0;

    if( x === undefined )
        return 1;

    if( y === undefined )
        return -1;

    const xString = toString(x);
    const yString = toString(y);

    if( xString < yString )
        return -1;

    if( xString > yString )
        return 1;

    return 0;
};

const toString = obj => {
    //ECMA specification: http://www.ecma-international.org/ecma-262/6.0/#sec-tostring

    if( obj === null )
        return "null";

    if( typeof obj === "boolean" ||  typeof obj === "number" )
        return (obj).toString();

    if( typeof obj === "string" )
        return obj;

    if( typeof obj === "symbol" )
        throw new TypeError();

    //we know we have an object. perhaps return JSON.stringify?
    return (obj).toString();
};

module.exports = defaultCompare;

How can I test it?

You can test this function like the following:

const arr = [ undefined, null, 3, 2, 'B', 'a', 'b', 'A',
{ hello: "world"}, { goodnight: 'moon'} ]

assertEql( arr.sort(), arr.sort(defaultCompare) ); //true

The outputs should be equal, provided you test them in the same browser.

like image 104
Flame_Phoenix Avatar answered Oct 13 '22 18:10

Flame_Phoenix