tags:

views:

93

answers:

3

If I have a self-referential Topic class and I want to recursively loop through all of my subtopics, what would be a good Ruby way to do that:

@category.topics.all(:parent_id => topic.id).each do |subtopic|
    subtopic.delete_tags
    @category.topics.all(:parent_id => subtopic.id).each do |subsubtopic|
    subsubtopic.delete_tags
        @category.topics.all(:parent_id => subsubtopic.id).each do |subsubsubtopic|
        subsubtopic.delete_tags
            @category.topics.all(:parent_id => subsubsubtopic.id).each do |subsubsubsubtopic|
            subsubsubtopic.delete_tags
            end
        end
    end
end
+1  A: 

Something like this?

class Topic
  def delete_subtopic_tags
    all(:parent_id => self.id).each do |subtopic|
      subtopic.delete_subtopic_tags
      subtopic.delete_tags
    end
  end
end
glenn mcdonald
Will this go into an infinite loop?
jprete
If any topics exist in the database with `parent_id` equal to their own `id`, then yes, it will.
rcoder
@jprete: It would only be infinite if some topic is a subtopic of itself. But that seems like an error in itself. Otherwise the recursion will end when it reaches a topic that has no subtopics. But it seems like it should be `:parent_id => self.id`.
Chuck
+1  A: 

Is this an ActiveRecord model class? If so, you should be able to cleanly recurse through child objects using something like the following code:

class Topic
  has_many :topics
  belongs_to :parent, :classname => "Topic", :foreign_key => 'parent_id'

  # ...other model class logic...
  def delete_tags_recursive
    delete_tags
    topics.each {|subtopic| subtopic.delete_tags_recursive }
  end
end

This has the additional benefit of letting you use the topics and parent methods created by the has_many and belongs_to decorators in order to easily walk the topic tree.

rcoder
A: 

I don't know Ruby, but if you have hash table and linked list implementations handy I'd do it something like this (in Java-ish pseudocode):

Topic currentTopic = topic;
list.add(currentTopic);
hashtable.add(currentTopic);
while (list.empty() == false) {
    for (Topic t : currentTopic.getSubtopics()) {
        if (hashtable.contains(t) == false) {
            list.add(t);
            hashtable.add(t);
        }
    }
    visit(currentTopic);
    list.remove(currentTopic);
    currentTopic = list.getFirst();
}

The major point here is to keep the hashtable list so that you can easily make sure that you never visit a Topic twice. Other than that, it's essentially an implementation of breadth-first search. visit() does whatever you need to do for each individual topic.

jprete