[Code] Sorting an array of class identifiers by ancestry


#1

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

#2

Hi Dan
No one has responded because you are so far ahead of everyone in this ruby game we would not know any different. Do you make ruby scripts to order have you an email address?


#3

It just an obscure abstract Ruby exercise. The Ruby Core team never thought of comparing class objects (other than whether they are the same objects via ==.)

But to me it’s natural to have the superclasses at the right end of the array, and the youngest subclass at the left end of the array. (I’m also a genealogist.)

The weird thing with putting this into Ruby is that my algorithm would need to be put into a <=> class method in class Class, but not a instance method or it’d be inherited by all sub-classes which are supposed to redefine it as needed.

Anyway, it was just interesting in a nerdy way.

This question makes no sense to me.


#4

Ok I get it, its kind of like the family tree. And so the code is best structured accordingly.
I have some ideas for Ruby but I don’t know the code much. just start learning about a month ago.
Here is a question for you Dan. Say I draw up a kitchen cabinet with doors and make it a group. Is there a script you could write that would analyse that model group for all its points and faces and convert it to a script file. Like “kitchen.ru” format so you can have your models in scripts rather than a sketchup file like Kitchen.skp… I was looking for you email address to see if you would be interested in writing some code for me.


Converting model to Ruby script