Boolean values have no natural ordering.
The Ruby language designer(s) probably felt that to invent an ordering for booleans would be a surprise to developers so they intentionally left out the comparison operators.
Pergunta
In "Is it possible to sort a list of objects depending on if the individual object's response to a method?", I discovered that the flying saucer doesn't work on booleans.
Consider:
Ruby 1.8.7:
[true, false].sort # => undefined method `<=>' for true:TrueClass (NoMethodError)
true <=> false # => undefined method `<=>' for true:TrueClass (NoMethodError)
Ruby 1.9.3:
[true, false].sort # => comparison of TrueClass with false failed (ArgumentError)
true <=> false # => nil
true <=> true # => 0
false <=> true # => nil
It may have something to do with true and false not having a canonical sort order, because which comes first? But, that sounds pretty weak to me.
Is this a bug in sort
?
Solução
The Ruby language designer(s) probably felt that to invent an ordering for booleans would be a surprise to developers so they intentionally left out the comparison operators.
Outras dicas
The so-called flying saucer requires all comparison operators (<
, >
, ==
) to work (not technically, although certainly theoretically). true
and false
are not less-than or greater-than each other. The same will hold true for nil
. For a practical workaround, you can 'cast' to integers (0 for false, 1 for true). Something like:
[true, false, true].sort_by{|e| e ? 1 : 0}
Booleans have no natural ordering. Unlike C, false is not less than true, they're just equivalent and equally valid states. However it is possible to configure the sort any way you like using a block, for example:
ary = [true, false, false, true]
ary.sort {|a,b| a == b ? 0 : a ? 1 : -1 }
# => [false, false, true, true]
Reversing the order is also trivial:
ary.sort {|a,b| a == b ? 0 : a ? -1 : 1 }
# => [true, true, false, false]
I know this is quite old, but this bit me recently. But it's Ruby, right? How about doing this monkey-patch?
#! /usr/bin/env ruby
class TrueClass
include Comparable
def <=>(other)
if other.class == FalseClass
1
elsif other.class == TrueClass
0
else
nil
end
end
end
class FalseClass
include Comparable
def <=>(other)
if other.class == TrueClass
-1
elsif other.class == FalseClass
0
else
nil
end
end
end
puts "true <=> false: #{true <=> false}"
puts "false <=> true: #{false <=> true}"
puts "true <=> true: #{true <=> true}"
puts "false <=> false: #{false <=> false}"
puts "true <=> 13: #{true <=> 13}"
puts "true > false: #{true > false}"
puts "true < false: #{true < false}"
puts "true == false: #{true == false}"
puts "true == true: #{true == true}"
puts "false == false: #{false == false}"
puts "false < true: #{false < true}"
puts "[false, true, false].max: #{[false, true, false].max}"
puts "[false, true, false].min: #{[false, true, false].min}"