Behavior of “Greater than” (and another inequality comparison operators) on arrays

Tags: , ,



I couldn’t find any description or any mention about how >, <, <= and >= operators behave while comparing two arrays in javascript.

The only trivial thing I could think of is that the two arrays are compared by both elements for each relative indexes, but after testing it – I didn’t got the result I expected.

So how arrays are being compared?

Few test cases:

console.log([1] > [2]); // FALSE - ok
console.log([2] > [1]); // TRUE - ok
console.log([2] > [2]); // FALSE - ok
console.log([1,2] > [2,3]); // FALSE - In case two elements in each index are compared, and the answer is "If all >" - ok  
console.log([2,2] > [1,1]); // TRUE - In case two elements in each index are compared, and the answer is "If all >" - ok
console.log([1,2] > [1,1]); // TRUE - In case two elements in each index are compared, and the answer is "If all >" - Unexpected
// More cases with 3 elements:
console.log([1,2,1] > [1,2,2]); // FALSE
console.log([1,1,1] > [1,1,2]); // FALSE
console.log([1,3,1] > [1,3,0]); // TRUE
console.log([1,1,1] > [1,1,2]); // FALSE

Answer

To understand the results of your test cases, you need to understand what happens when you compare arrays or any other object using any relational operator.

Long story short, objects are converted to strings before they are compared using any of the relational operator.

(skip to the end of the answer if you don’t want to read about object-to-primitive conversion.)

ToPrimitive abstract operation

To convert objects to primitive values, javascript performs toPrimitive abstract operation that takes two arguments:

  • input: object that should be converted to a primitive value
  • preferredType: optional second argument that specifies the type that should be favoured when converting an object to a primitive value

For object to primitive conversion, toPrimitive abstract operation invokes another abstract operation known as OrdinaryToPrimitive

OrdinaryToPrimitive abstract operation

For object to primitive conversion, toPrimitive abstract operation invokes OrdinaryToPrimitive abstract operation with two arguments:

  • O: object that should be converted to a primitive value
  • hint: type that should be favored when converting the object to a primitive value

toPrimitive abstract operation sets the hint as following:

  • If the preferredType is a string, set hint to string
  • If the preferredType is a number, set hint to number
  • If preferredType is not specified, set hint to number

OrdinaryToPrimitive abstract operation uses following three algorithms to convert the object to a primitive value:

  • prefer-string: If hint is string, return a primitive value, preferring a string value, if conversion to string is possible

  • prefer-number: If hint is number, return a primitive value, preferring a number value, if conversion to number is possible

  • no-preference: This algorithm expresses no preference about what type of primitive value should be returned and lets the objects define what type of primitive value should be returned. If hint is default or there is no hint, this algorithm is used to convert an object to a primitive value.

    It allows objects to override the default ToPrimitive behavior. Among the built-in objects, only Date and Symbol objects override the default ToPrimitive behavior. Date and Symbol objects implement this algorithm as prefer-string whereas all the other built-in objects implement this algorithm as prefer-number (Objects can override the default ToPrimitive behavior by implementing Symbol.toPrimitive method.)

All objects inherit two methods that are used to convert objects to primitive values. These two methods are:

  • .valueOf()
  • .toString()

object to primitive conversion involves calling above mentioned methods and the object to primitive conversion algorithms mentioned above, call these two methods in different order.

prefer-string

This algorithm first calls the .toString() method and if the resulting value is a primitive value, javascript uses the returned primitive value, even if it’s not a string. If the .toString() method doesn’t exists or it returns an object, then .valueOf() method is called. If .valueOf() method returns a primitive value, then that value is used otherwise TypeError is thrown.

prefer-number

Only difference between this algorithm and prefer-string is that it first tries .valueOf() method and then .toString() method.

no-preference

When there is no preferred type or hint or if the preferred type is default, by default, prefer-number algorithm is used. Objects can override this behaviour and of all the built-in objects, only Date and Symbol override this default ToPrimitive conversion behaviour. Date and Symbol use prefer-string algorithm when there is no preferred type or a hint or the preferred type is default.


Now coming back to you question, relational operators, i.e. <, >=, <, <= can be used to compare strings as well as numbers. If either operand of these operators is an object, it is converted to primitive value using prefer-number algorithm. So when you compare two arrays using a relational operator, javascript tries to convert each array into primitive value using prefer-number algorithm.

As mentioned above, prefer-number algorithm first calls .valueOf() method. If return value is a primitive, that value is used otherwise .toString() method is called. Default .valueOf() method simply returns the object itself rather than returning a primitive value, so javascript always ends up calling .toString() method when it uses prefer-number algorithm.

When .toValue() method is called on an array, it simply returns the array on which this method was called. Javascript then calls .toString() method on this returned array. When .toString() method is called on an array, it converts all the elements of the array into strings and then joins all the strings together with commas in between each string.

So when you compare [1] > [2], you are comparing '1' > '2' and similarly [1,2] > [1,1] is converted to '1,2' > '1,1'. As strings are compared by their Unicode code points, '1' > '2' evaluates to false and '1,2' > '1,1' evaluates to true.



Source: stackoverflow