Topic: Sorting

Hello,
I'm trying to figure this problem out. I have a table with a list of items. I'm trying to create a hierarchy between them so I also have a parent_id. For instance

ID    Name           Parent_id
1      Name1         null
2      Name2         1
3      Name3         1
4      Name4         null


I'm trying to get rails to display this as:
Name1
    Name2
    Name3

Name4


I don't know if this is a rails question or a ruby question, but I can't figure out how to call either the right query or order it in an array correctly.

Any help would be much appreciated.

Thank you

Re: Sorting

It depends on how your models are setup. When using single table in heritance, you should still setup each individual model correctly. As an STI table is supposed to contain a column for what class it belongs to. See here as an example:

http://juixe.com/techknow/index.php/200 … heritance/

That said, I did something a bit similar where i have books that have a status. For sale, in transaction, sold. I wanted to show headings for each of those, and list all the books under it. I ended up doing it in the view.

<% books_list.group_by(&:status).sort.each do |status, books| %>
  <h3><%= status %></h3>
  <% books.each do |book| %>
    <%= book %>
  <% end %>
<% end %>

That code there groups by the status column and gives me the status to work with. That said, because your records are referencing other records in the same table, im not sure how you would implement it exactly.

I would review why you have structured your table/models that way. If you could give us some insight of what you wish to accomplish, we might be able to walk you through it better.

Re: Sorting

Basically, there's a list of "tags" and they want to do a hierarchy. I know that really doesn't explain it much better, and I can't really understand why they want to do it, but they want it, when I print it out, to have subheadings and headings and indentations as such (by they I mean the client). I don't really want to create another table for "headings" because the headings themselves are tags. Is there a better way to do this?

Thanks

Re: Sorting

I looked at what you sent me. The problem is that tags have specific headings that are also tags, but not all tags are in the same heading; meaning:

The example you showed me there are administrators and there are users. There is no way to simplify this in the tags as tags that are "heading" tags don't have every child tag under them, only those where the parent_id is the id of the heading tag

Re: Sorting

Look at the acts_as_tree plugin, it does exactly what you're trying to do.

Last edited by Max Williams (2010-07-12 05:37:09)

###########################################
#If i've helped you then please recommend me at Working With Rails:
#http://www.workingwithrails.com/person/ … i-williams

Re: Sorting

Max Williams wrote:

Look at the acts_as_tree plugin, it does exactly what you're trying to do.

I started looking into that, but we're trying to keep the application as light on plugins as possible. In the meantime, I came up with something like this. If anyone can see a flaw here, please let me know:

  def sort_tags (userid)
    tags = Skill.find_all_by_user_id(userid)
    for tag in tags
        ret += tag.name
        ret += findAllChildren(tag.id)
      #end
    end
  end
  
  def findAllChildren(parentTag)
    @childtags = Skill.find_all_by_parent_id(parentTag)
    for childtag in @childtags
      ret += childtag.name
    end
  end

Re: Sorting

flaws in #comments

OmriShiv wrote:
Max Williams wrote:

Look at the acts_as_tree plugin, it does exactly what you're trying to do.

I started looking into that, but we're trying to keep the application as light on plugins as possible. In the meantime, I came up with something like this. If anyone can see a flaw here, please let me know:

  def sort_tags (userid)
    tags = Skill.find_all_by_user_id(userid)
    for tag in tags
        #you use ret here without defining it first
        ret += tag.name
        ret += findAllChildren(tag.id)
      #end
    end
    #you return without doing anything with ret
  end
  
  def findAllChildren(parentTag)
    @childtags = Skill.find_all_by_parent_id(parentTag)
    #there's no need to define an instance variable here
    for childtag in @childtags
      #again ret is used without being defined
      ret += childtag.name
    end
    #again you don't do anything with ret at the end.
  end

If you don't want to use acts_as_tree then you can at least implement a couple useful methods.  eg

#Tag
  has_many :children, :class_name => "Tag", :foreign_key => "parent_id"
  belongs_to :parent, :class_name => "Tag", :primary_key => "parent_id"
  belongs_to :user

#User
  has_many :tags

As for your methods, it's not clear what you actually want to do.  I would do something like this to show them on a web page

<ul class="level1">
  <% current_user.tags.each do |tag| %>
    <li><%= tag.name %>
      <% if (children = tag.children).size > 0 %>
        <ul class="level2">
          <% children.each do |child_tag| %>
            <li><%= child_tag.name %></li>
          <% end %>
        </ul>
      <% end %>
    </li>
  <% end %>
</ul>

###########################################
#If i've helped you then please recommend me at Working With Rails:
#http://www.workingwithrails.com/person/ … i-williams

Re: Sorting

I decided to try out acts_as_tree; however, I feel like I am missing something. I can't find a tutorial that clearly explains how to do the display. I have this in my model:

    acts_as_tree :order=> "name"

and in my view:

<% for tag in @tag %>
    <%=h tag.name %>
<br />
<% end %>

Re: Sorting

Acts as tree doesn't give you any view code - how you display it is up to you.  acts_as_tree is there simply to help you manage the associations between the objects.  My suggested view code, which creates a nested unordered list, seemed like the simplest way to do what you had originally, where the children were indented from the parents.

###########################################
#If i've helped you then please recommend me at Working With Rails:
#http://www.workingwithrails.com/person/ … i-williams

Re: Sorting

So, does your view code actually require acts_as_tree? It doesn't seem like it

Re: Sorting

OmriShiv wrote:

So, does your view code actually require acts_as_tree? It doesn't seem like it

No, my view was designed to work with acts_as_tree *or* any other approach that gives tags a method 'children' which returns that tag's children - such as the has_many :children line i featured in my code.

###########################################
#If i've helped you then please recommend me at Working With Rails:
#http://www.workingwithrails.com/person/ … i-williams

Re: Sorting

Great, thank you. I'm getting some nomethod errors, but that's coming from my lack of understanding the rails models, I'll keep trying and post again later if I'm having issues. In the mean time, I've been wracking my brain trying to figure out my other issues so I'll post a link to it here if anyone can take a look at it:

http://railsforum.com/viewtopic.php?pid=127642#p127642

Re: Sorting

I got it to work; however, it's giving me duplicates of the children. The children show up as subheadings of the parent tag, but also show up as parent tags themselves.

Re: Sorting

OmriShiv wrote:

I got it to work; however, it's giving me duplicates of the children. The children show up as subheadings of the parent tag, but also show up as parent tags themselves.

ah right, that's probably because i assumed that it was only the parents that belonged to the user.  see where i'm saying:

  <% current_user.tags.each do |tag| %>

you could change that to

  <% current_user.tags.find_all_by_parent_id(nil).each do |tag| %>

###########################################
#If i've helped you then please recommend me at Working With Rails:
#http://www.workingwithrails.com/person/ … i-williams

Re: Sorting

Max Williams wrote:
OmriShiv wrote:

I got it to work; however, it's giving me duplicates of the children. The children show up as subheadings of the parent tag, but also show up as parent tags themselves.

ah right, that's probably because i assumed that it was only the parents that belonged to the user.  see where i'm saying:

  <% current_user.tags.each do |tag| %>

you could change that to

  <% current_user.tags.find_all_by_parent_id(nil).each do |tag| %>


That's fantastic, thank you so much!

Re: Sorting

OmriShiv wrote:
Max Williams wrote:
OmriShiv wrote:

I got it to work; however, it's giving me duplicates of the children. The children show up as subheadings of the parent tag, but also show up as parent tags themselves.

ah right, that's probably because i assumed that it was only the parents that belonged to the user.  see where i'm saying:

  <% current_user.tags.each do |tag| %>

you could change that to

  <% current_user.tags.find_all_by_parent_id(nil).each do |tag| %>


That's fantastic, thank you so much!

Ypu're welcome - this is actually more efficient as we eager-load the children:

  <% current_user.tags.find_all_by_parent_id(nil, :include => [:children]).each do |tag| %>

###########################################
#If i've helped you then please recommend me at Working With Rails:
#http://www.workingwithrails.com/person/ … i-williams

Re: Sorting

Great, thank you.

Did you by any chance get to check my other post? I can't figure out why something that works flawlessly throughout the rest of my application evaluates nil up there?

Re: Sorting

Sorry to resurrect this thread, but I'm trying to figure out if this works for children of children? I have:

Heading
  Subheading
     Child of subheading

Is there a way to change the view code to make it indifferent to the depth?

Thank you

Re: Sorting

I added another search for children, but this seems really inelegant. There has to be a recursive search for all depths of children, no?

Re: Sorting

OmriShiv wrote:

I added another search for children, but this seems really inelegant. There has to be a recursive search for all depths of children, no?

If you want to do this then you should have a look at acts_as_nested_set instead, which is like acts_as_tree but uses a data structure that lets you get all the descendants of a tree node in one query.

###########################################
#If i've helped you then please recommend me at Working With Rails:
#http://www.workingwithrails.com/person/ … i-williams