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
Advertisement
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 valuepreferredType
: 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 valuehint
: 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 astring
, sethint
tostring
- If the
preferredType
is anumber
, sethint
tonumber
- If
preferredType
is not specified, sethint
tonumber
OrdinaryToPrimitive abstract operation uses following three algorithms to convert the object to a primitive value:
prefer-string: If
hint
isstring
, return a primitive value, preferring a string value, if conversion to string is possibleprefer-number: If
hint
isnumber
, return a primitive value, preferring a number value, if conversion to number is possibleno-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
isdefault
or there is nohint
, 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, onlyDate
andSymbol
objects override the defaultToPrimitive
behavior.Date
andSymbol
objects implement this algorithm asprefer-string
whereas all the other built-in objects implement this algorithm asprefer-number
(Objects can override the defaultToPrimitive
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 invokes .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 a primitive value using prefer-number
algorithm.
As mentioned above, prefer-number
algorithm first calls .valueOf()
method. If return value is a primitive value, that value is used, otherwise .toString()
method is called.
Default implementation of .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
.