This three-way comparison operator has the nickname spaceship operator. It is available as of PHP 7.
In version 2.12 of Twig, the developers added a spaceship operator. Craft added support for Twig 2.12 in Craft CMS 3.3.8.
The Spaceship Operator doesn’t allow you to send an array to Mars, sadly, but it does allow you to make multiple comparisons at the same time. And by multiple I mean three.
It kind of looks like a spaceship, I guess:
<==>
Or maybe a flying saucer?
This is also called a three-way comparison operator. It has the nickname spaceship operator in the PHP documentation and it’s of the comparison operator type. It is available as of PHP 7.
The result of the comparison isn’t a boolean; instead, it’s an integer of 0, 1, or ‑1 depending on how the comparison evaluates.
Let’s look at a simple example by comparing two integers:
23 <=> 23
20 <=> 20
What happens when this is run by Twig or PHP — or in any one of the several languages that support three-way comparison operators — is that it first
This does three checks:
We can do this in Twig by using the output tag and a spaceship comparison.
{{ 23 <=> 23 }}
{# returns 0 #}
{{ 23 <=> 6 }}
{# returns 1 #}
{{ 23 <=> 50 }}
{# returns -1 #}
That’s a nice theoretical exercise but how do we apply this in a real usage scenario?
A typical example of using the spaceship operator is for sorting. In Twig we have the sort
filter, which uses PHP’s asort
function for sorting arrays.
asort
is based on QuickSort, which is a divide and conquers sorting algorithm. It splits the array at a pivot point and then swaps array elements based on value comparisons.
Here’s the data we’ll use for this example. It’s an array of runners and their marathon time in seconds.
{% set runners = [
{ name: 'Wilson Kipsang', time: 7403 },
{ name: 'Eliud Kipchoge', time: 7299 },
{ name: 'Patrick Makau', time: 7418 },
{ name: 'Dennis Kimetto', time: 7377 },
{ name: 'Haile Gebrselassie', time: 7439 },
] %}
Let’s loop through them to get a list of runners:
{% for runner in runners %}
<li>{{ runner.name }} - {{ runner.time }}</li>
{% endfor %}
But if I want to order them by time (stored in seconds), how would I do that? We use the arrow function (a shorthand way of creating an anonymous function) in Twig and the sort filter and the spaceship operator.
{% for runner in runners | sort((a,b) => a.time <=> b.time) | column('name') %}
<li>{{ runner }}</li>
{% endfor %}
This outputs the runners sorted by time, smallest to biggest. When the numbers are the same the algorithm leaves the original order in place.
Eliud Kipchoge
Dennis Kimetto
Wilson Kipsang
Patrick Makau
Ryan Irelan
Meb Keflezki
Haile Gebrselassie
In the for
-loop, we pass the runners through the sort
filter using an arrow function. We tell sort
to compare two times at once (a
and b
) and use the spaceship comparison to sort them by time. If any of the times are the same then it doesn’t do anything but does sort the others based on time.
If we didn’t use the spaceship operator and instead used >=
then we’d get the same results for everything except those that have the same time value.
{% for runner in runners | sort((a,b) => a.time >= b.time) | column('name') %}
<li>{{ runner }}</li>
{% endfor %}
Those items with the same time
value would sort by the value of their name
. You can see that in the output.
Eliud Kipchoge
Dennis Kimetto
Wilson Kipsang
Patrick Makau
Haile Gebrselassie
Meb Keflezki
Ryan Irelan
When we change it back to the spaceship operator than we get the array order of the items with the value time
value left intact.
Eliud Kipchoge
Dennis Kimetto
Wilson Kipsang
Patrick Makau
Ryan Irelan
Meb Keflezki
Haile Gebrselassie