Search code examples
javascriptarraysthree-way-merge

Three-way compare function for Arrays in Javascript


There have been other questions about How to compare arrays in JavaScript?. What I want to know is the most straightforward way to write/use a three-way comparison function like the one required by Array.sort(). Here's an example using the default one which doesn't work well:

> [ [4,5,10], [4,5,6], [4,1,2] ].sort() // no compare function, uses the default one
[ [ 4, 1, 2 ],
  [ 4, 5, 10 ], // oops, string sorting makes 10 < 6
  [ 4, 5, 6 ] ]

This is what I came up with:

// return -1 if lhs is "less" than rhs, +1 if "greater", and 0 if equal
// if lhs and rhs have different lengths, only the shorter part will be considered
function compareArrays(lhs, rhs) {
  for (var ii = 0; ii < lhs.length; ii++) {
    if (lhs[ii] < rhs[ii]) {
      return -1;
    } else if (lhs[ii] > rhs[ii]) {
      return 1;
    }
  }
  return 0;
}

Which gives us what we want:

> [ [4,5,10], [4,5,6], [4,1,2] ].sort(compareArrays)
[ [ 4, 1, 2 ],
  [ 4, 5, 6 ],
  [ 4, 5, 10 ] ]

Is there something more like a one-liner, or must I define my own function whenever I want to do this?

Supporting old browsers is not a requirement. Using libraries like jQuery or Underscore is OK.

One way to look at this is "The first nonzero value from a standard three-way compare applied to each pair of elements." But even then I haven't found a good fit in existing libraries.


Solution

  • I'd go with a generic comparison-maker function to be used in a functional way:

    function compareArrays(compareItems) {
        return function(a, b) {
            for (var r, i=0, l=Math.min(a.length, b.length); i<l; i++)
                if (0 != (r = compareItems(a[i], b[i])))
                    return r;
            return a.length - b.length;
         };
    }
    // Examples:
    var compareNumberArrays = compareArray(function(a,b){ return a-b; }),
        compareGenericArrays = compareArray(function(a,b){ return +(a>b)||-(b>a); });
    

    Now you can use

    [ [4,5,10], [4,5,6], [4,1,2], [4,5] ].sort(compareNumberArrays)
    

    Is there something more like a one-liner, or must I define my own function whenever I want to do this?

    Comparing arrays is too complex for a one-liner, you should use a helper function. There's no built-in one that you can use everywhere.