The spaceship operator <=> in Ruby

If you’re a Ruby noob like me, you probably saw the “spaceship operator” and thought of Space Invader or Darth Vader’s infamous TIE fighter. I am currently on day 2 of learning Ruby going into day 3 and I had trouble understanding what this interestin…


This content originally appeared on DEV Community and was authored by Jblengino510

If you're a Ruby noob like me, you probably saw the "spaceship operator" and thought of Space Invader or Darth Vader's infamous TIE fighter. I am currently on day 2 of learning Ruby going into day 3 and I had trouble understanding what this interesting looking operator does and what you could use it for. Hopefully by the end of this post, you and I can both take away some practical use cases for this method.

Ruby Comparison Operators

Before we start experimenting with the <=> operator, let's take a look at more common comparison operators.

 1 > 0     #=> true     (Greater than)
 0 < 1     #=> true     (Less than)
 1 >= 0    #=> true     (Greater or equal than)
 0 <= 1    #=> true     (Less or equal than)
 1 == 0    #=> false    (Equals)
 1 != 0    #=> true     (Not equals)

Pretty straightforward, they all return a boolean value. An important takeaway from one of my readings was to remember that all Ruby operators are actually methods. This is because the behavior (return value) will depend on the type of class it is called on.

<=> Operator

It's technical name is the combined comparison operator. The major difference compared to the other comparison operators is that it returns 3 different values: (-1), (0), or (1).

1 <=> 1     #=> 0
0 <=> 1     #=> -1
1 <=> 0     #=> 1

For Integers: (0) if right side = left, (-1) if less than, and (1) if greater than.

"string" <=> "string"     #=> 0     (contents are the same)
"string" <=> "stringg"    #=> -1    (length of string)
"string1" <=> "string2"   #=> -1    (string integer)
"STRING" <=> "string"     #=> -1    (capital)

For Arrays the behavior is not as straight forward. It looks like uppercase letters are given a lower value than lowercase letters. Why?



Another great post on this topic mentions how characters in strings are compared with binary values under the hood, which helps to better understand the above return values.

fighter1 = {type: "TIE Fighter", owner: "Darth Vader", affiliation: "Galactic Empire", id: 1}
fighter2 = {type: "X-wing", owner: "Luke Skywalker", affiliation: "Rebel Alliance", id: 2}

fighter1 <=> fighter1     #=> 0
fighter2 <=> fighter1     #=> nil
fighter1 <=> fighter2     #=> nil

For Hashes it's even less clear...

fighter1 = {type: "TIE Fighter", owner: "Darth Vader", affiliation: "Galactic Empire", id: 1}
fighter2 = {type: "TIE Fighter", owner: "Darth Vader", affiliation: "Galactic Empire", id: 1}

fighter1 <=> fighter1     #=> 0
fighter2 <=> fighter1     #=> 0
fighter1 <=> fighter2     #=> 0

Here the contents are exactly the same.

fighter1 = {type: "TIE Fighter", owner: "Darth Vader", affiliation: "Galactic Empire", id: 1}
fighter2 = {type: "X-wing", owner: "Luke Skywalker", affiliation: "Rebel Alliance", id: 2}

fighter1[:owner] <=> fighter1[:owner]     #=> 0
fighter2[:owner] <=> fighter1[:owner]     #=> 1
fighter1[:owner] <=> fighter2[:owner]     #=> -1

Here it looks like the string length comes in to play after changing fighter2 back to normal.

fighter1 = {type: "TIE Fighter", owner: "Darth Vader", affiliation: "Galactic Empire", id: 1}
fighter2 = {type: "X-wing", owner: "Luke Skywalker", affiliation: "Rebel Alliance", id: 2}

fighter1[:id] <=> fighter1[:id]     #=> 0
fighter2[:id] <=> fighter1[:id]     #=> 1
fighter1[:id] <=> fighter2[:id]     #=> -1

Even though these examples do no showcase every possible scenario for these data types, we now know that the behavior of the <=> operator for Arrays and Hashes is a little unpredictable depending on their respective contents. Even Ruby's documentation mentions how unpredictable the return value could be based on the objects being compared.
That's not still not very helpful for how we could use this in a practical way.

Using <=> with Ruby's sort method

star_fighters = [
  {type: "TIE Fighter", owner: "Darth Vader", affiliation: "Galactic Empire", id: 1}, 
  {type: "X-wing", owner: "Luke Skywalker", affiliation: "Rebel Alliance", id: 2},
  {type: "Jedi Starfighter", owner: "Obi-Wan Kenobi", affiliation: "The Republic", id: 3}

]

sorted_star_fighters = star_fighters.sort do |fighter1, fighter2|
  fighter1[:id] <=> fighter2[:id]
end
puts sorted_star_fighters
#=> {:type=>"TIE Fighter", :owner=>"Darth Vader", :affiliation=>"Galactic Empire", :id=>1}, {:type=>"X-wing", :owner=>"Luke Skywalker", :affiliation=>"Rebel Alliance", :id=>2}, {:type=>"Jedi Starfighter", :owner=>"Obi-Wan Kenobi", :affiliation=>"The Republic", :id=>3}

In this scenario, our hashes are being ordered from lowest to highest because based on how our id's are set up, fighter2's id will always be greater than fighter1's, which will return -1.

star_fighters = [
  {type: "TIE Fighter", owner: "Darth Vader", affiliation: "Galactic Empire", id: 1}, 
  {type: "X-wing", owner: "Luke Skywalker", affiliation: "Rebel Alliance", id: 2},
  {type: "Jedi Starfighter", owner: "Obi-Wan Kenobi", affiliation: "The Republic", id: 3}

]

sorted_star_fighters = star_fighters.sort do |fighter1, fighter2|
  fighter2[:id] <=> fighter1[:id]
end
puts sorted_star_fighters
#=> {:type=>"Jedi Starfighter", :owner=>"Obi-Wan Kenobi", :affiliation=>"The Republic", :id=>3}, {:type=>"X-wing", :owner=>"Luke Skywalker", :affiliation=>"Rebel Alliance", :id=>2}, {:type=>"TIE Fighter", :owner=>"Darth Vader", :affiliation=>"Galactic Empire", :id=>1}

In this scenario, our hashes are being ordered from highest to lowest because in this case, fighter2's id will always be greater than fighter1's id, which will return 1.

Takeaways

  1. Return values are integers: (-1), (0), or (1)
  2. Return values can be unpredictable with Arrays & Hashes depending on their contents.
  3. The <=> operator can come in handy when using sort to order your data from ascending or descending order in a cleaner way instead of doing something like this:
star_fighters = [
  {type: "TIE Fighter", owner: "Darth Vader", affiliation: "Galactic Empire", id: 1}, 
  {type: "X-wing", owner: "Luke Skywalker", affiliation: "Rebel Alliance", id: 2},
  {type: "Jedi Starfighter", owner: "Obi-Wan Kenobi", affiliation: "The Republic", id: 3}

]

sorted_star_fighters = star_fighters.sort do |fighter1, fighter2|
  if fighter1[:id] == fighter2[:id]
    0
  elsif fighter1[:id] < fighter2[:id]
    -1
  elsif fighter1[:id] > fighter2[:id]
    1
  end
end
puts sorted_star_fighters
#=> {:type=>"TIE Fighter", :owner=>"Darth Vader", :affiliation=>"Galactic Empire", :id=>1}, {:type=>"X-wing", :owner=>"Luke Skywalker", :affiliation=>"Rebel Alliance", :id=>2}, {:type=>"Jedi Starfighter", :owner=>"Obi-Wan Kenobi", :affiliation=>"The Republic", :id=>3}

Conclusion

Based on my short experience with the <=> operator, it seems to be most useful in tandem with the sort enumerator. I would love to hear if anyone else knows any other use cases in the comments below. Hopefully this post gave you a slightly better understanding of the <=> operator. I am definitely going to continue to experiment with it. Other languages that use the <=> operator include: Perl, Apache, Groovy, PHP, Eclipse, Ceylon, and C++. Having a good grasp on how and when to use conditional operators to compare values will allow you to control the flow of your data like a jedi controlling the flow of The Force.

Resources

Ruby equality
Ruby operators
Ruby sort enumerator
https://www.youtube.com/watch?v=tpsdxtf01po
https://www.youtube.com/watch?v=f4NItw7r33E


This content originally appeared on DEV Community and was authored by Jblengino510


Print Share Comment Cite Upload Translate Updates
APA

Jblengino510 | Sciencx (2021-08-11T20:41:02+00:00) The spaceship operator <=> in Ruby. Retrieved from https://www.scien.cx/2021/08/11/the-spaceship-operator-in-ruby/

MLA
" » The spaceship operator <=> in Ruby." Jblengino510 | Sciencx - Wednesday August 11, 2021, https://www.scien.cx/2021/08/11/the-spaceship-operator-in-ruby/
HARVARD
Jblengino510 | Sciencx Wednesday August 11, 2021 » The spaceship operator <=> in Ruby., viewed ,<https://www.scien.cx/2021/08/11/the-spaceship-operator-in-ruby/>
VANCOUVER
Jblengino510 | Sciencx - » The spaceship operator <=> in Ruby. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/08/11/the-spaceship-operator-in-ruby/
CHICAGO
" » The spaceship operator <=> in Ruby." Jblengino510 | Sciencx - Accessed . https://www.scien.cx/2021/08/11/the-spaceship-operator-in-ruby/
IEEE
" » The spaceship operator <=> in Ruby." Jblengino510 | Sciencx [Online]. Available: https://www.scien.cx/2021/08/11/the-spaceship-operator-in-ruby/. [Accessed: ]
rf:citation
» The spaceship operator <=> in Ruby | Jblengino510 | Sciencx | https://www.scien.cx/2021/08/11/the-spaceship-operator-in-ruby/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.