Ruby - Spaceship operator will not work in block for .sort
I am getting an error when I try to use a non-alpha numeric spaceship operator in a sort function.
word = "out-classed"
letters = word.downcase.split('')
letters.sort! do |x, y|
if y < 'a'
next
else
value = x <=> y
end
end
I get ArgumentError: comparison of String with String failed
, and I'm pretty sure this is happening with the spacecraft operator and not with <comparison.
The interesting part is that when I do the same comparison in irb outside the context of the collation, the comparison works. It also works when the word variable is only letters.
Can someone help me understand why this doesn't only work in this particular context?
source to share
If you try to sort the collection, it x<=>y
should return 0, 1, or -1 for each pair of items in the collection. If <=>
defined artificially for some pairs (for example, 'a'<=>'-' #=> 0
and '-'<=>'a' #=> 0
), your type may return erroneous results.
This is because sorting algorithms do not necessarily evaluate all pairs of items in a collection. If, for example, he finds that:
'a' <=> 'b' #=> 0
and
'b' <=> 'c' #=> 0
he will conclude that:
`a` <=> `c` #=> 0
because sorting the collection must satisfy the transitivity: x <== z
if x <= y
and y <= z
.
For example, if the collection is an array ['z', '-', 'a']
and it detects that 'z' <= '-'
and '-' <= 'a'
, it concludes that 'z' <= 'a'
(and does not evaluate 'z' <=> 'a'
).
That's why:
['z', '-', 'a'].sort { |x,y| p [x,y]; (y < 'a') ? 0 : x<=>y }
#-> ["z", "-"]
#-> ["-", "a"]
#=> ["z", "-", "a"]
does not work. You have two options:
Remove items before sorting:
['z', '-', 'a'].select { |c| ('a'..'z').cover?(c) }.
sort { |x,y| (y < 'a') ? 0 : x<=>y }
#=> ["a", "z"]
or sort all elements of the collection:
['z', '-', 'a'].sort
#=> ["-", "a", "z"]
If the collection contains non-comparable elements (for example [1,2,'cat']
), you only have to remove the elements from the array until all other elements are comparable.
source to share