I had an interesting problem and thought I’d share it.
Scenario: Given an array of class identifiers in any random order, sort them according to generation from youngest (first) to oldest (last.)
We cannot just use plain-Jane Array#sort
because the <=>
method does not work at that class level. It only returns 1
(if a === b
,) or nil
which triggers a TypeError
with a "comparison of Class with Class failed"
message.
So we must use the block form of Array#sort
and write an algorithm that takes the place of the <=>
method, returning -1
, 0
or 1
just as that method would.
def sort_by_ancestry( ary, verbose=false )
#
puts if verbose
ary.sort {|a,b|
#
aline = a.ancestors[1..-1]
bline = b.ancestors[1..-1]
#
puts "sort: comparing #{a} and #{b} ..." if verbose
if aline == bline
puts "sort: #{a} and #{b} are equal, return 0." if verbose
0 # a.eql?(b)
elsif aline.include?(b)
puts "sort: #{a} less than #{b}, return -1." if verbose
-1 # a < b
elsif bline.include?(a)
puts "sort: #{a} greater than #{b}, return +1." if verbose
1 # a > b
else
#
result = nil
aline.each_with_index {|ac,i|
bline.each_with_index {|bc,j|
if ac == bc
# The higher the index, the farther
# from the common progenitor it is.
break result = 0 if i == j
break result = -1 if i > j
break result = 1 if i < j
end
}
break if result
}
#
if result
if verbose
case result
when -1
puts "sort: #{a} farther from common progenitor"<<
" than #{b}, return -1."
when 0
puts "sort: #{a} and #{b} are equidistant"<<
" from a common progenitor, return 0."
when 1
puts "sort: #{a} closer to common progenitor"<<
" than #{b}, return +1."
end
end
result
else
# Should not happen as all classes have either
# Object or BasicObject as common progenitor
puts "sort: #{a} and #{b} uncomparable!" if verbose
nil # will cause a TypeError "compare Class with Class failed."
end
#
end
}
end