# Is possible to order a nested model?

**URL:** <https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810>\
**Category:** Rails\
**Created:** [January 14, 2007, 11:28pm UTC](https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810 "2007-01-14T23:28:19Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![marek](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/b2d939/32.png) [@marek](https://www.ruby-forum.com/u/marek)\
**Post date:** [January 14, 2007, 11:28pm UTC](https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810/1 "2007-01-14T23:28:19Z")

</div>

I have a menu, that is hierarchical and stored according to nested set  
model. i want to do this to have each “level” ordered alphabetically,  
like this:

-country  
–Andorra  
–Belgium  
–Norway  
–Sweden  
-food  
–English  
–Swedish  
–Thai

In the example Country and Food must be ordered alphabetically, inside  
country those countries must be ordered alphabetically and the cuisines  
within food must be ordered alphabetically.

Is that possible?

Hope for help!

---

<div class="post-metadata">

**Author:** ![marek](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/b2d939/32.png) [@marek](https://www.ruby-forum.com/u/marek)\
**Post date:** [January 15, 2007, 1:00am UTC](https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810/2 "2007-01-15T01:00:43Z")

</div>

marek wrote:

> I have a menu, that is hierarchical and stored according to nested set  
> model. i want to do this to have each “level” ordered alphabetically,  
> like this:
> 
> -country  
> –Andorra  
> –Belgium  
> –Norway  
> –Sweden  
> -food  
> –English  
> –Swedish  
> –Thai
> 
> In the example Country and Food must be ordered alphabetically, inside  
> country those countries must be ordered alphabetically and the cuisines  
> within food must be ordered alphabetically.
> 
> Is that possible?
> 
> Hope for help!

It depends a bit on how you are storing the menu items.

If they are all in one multi-dimensional array, try this:

a = [[“4”,“3”,“2”,“1”],[“2”,“3”,“1”]]  
=\> [[“4”,“3”,“2”,“1”],[“2”,“3”,“1”]]  
a.collect{|x| x.sort}  
=\> [[“1”,“2”,“3”,“4”],[“1”,“2”,“3”]]

---

<div class="post-metadata">

**Author:** ![marek](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/b2d939/32.png) [@marek](https://www.ruby-forum.com/u/marek)\
**Post date:** [January 15, 2007, 2:22pm UTC](https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810/3 "2007-01-15T14:22:28Z")

</div>

Andrew S. wrote:

> marek wrote:
> 
> > I have a menu, that is hierarchical and stored according to nested set  
> > model. i want to do this to have each “level” ordered alphabetically,  
> > like this:
> > 
> > -country  
> > –Andorra  
> > –Belgium  
> > –Norway  
> > –Sweden  
> > -food  
> > –English  
> > –Swedish  
> > –Thai
> > 
> > In the example Country and Food must be ordered alphabetically, inside  
> > country those countries must be ordered alphabetically and the cuisines  
> > within food must be ordered alphabetically.
> > 
> > Is that possible?
> > 
> > Hope for help!
> 
> It depends a bit on how you are storing the menu items.
> 
> If they are all in one multi-dimensional array, try this:
> 
> a = [[“4”,“3”,“2”,“1”],[“2”,“3”,“1”]]  
> =\> [[“4”,“3”,“2”,“1”],[“2”,“3”,“1”]]  
> a.collect{|x| x.sort}  
> =\> [[“1”,“2”,“3”,“4”],[“1”,“2”,“3”]]

i’m storing it with create the category and then add it to a root or  
parent category so i have for example

root  
-country  
–andorra  
–norway  
–belgium  
-food  
–thai  
–english

i retrieve all them (to use with a select) with Category.find(:all,  
:order =\> “lft”).map{|category| ["-" \* category.depth + category.name,  
category.id]}  
and i have something like: [[“root”, 4], ["-contry", 5], ["–andorra",  
7], ["–norway", 11], ["–belgium", 12], ["-food",14], ["–thai", 15],  
["–english", 18]]  
how can i get them, like your example, in a multidimensional array?

---

<div class="post-metadata">

**Author:** ![marek](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/b2d939/32.png) [@marek](https://www.ruby-forum.com/u/marek)\
**Post date:** [January 16, 2007, 1:46am UTC](https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810/4 "2007-01-16T01:46:48Z")

</div>

cover wrote:

> and i have something like: [[“root”, 4], ["-contry", 5], ["–andorra",  
> 7], ["–norway", 11], ["–belgium", 12], ["-food",14], ["–thai", 15],  
> ["–english", 18]]  
> how can i get them, like your example, in a multidimensional array?

Your function is returning an array comprised of 2 dimensional arrays  
(the formatted menu item and the id). This make it a little tricky to  
sort it at this point so I would sort it at creation.

Get the database to do it for you:

Category.find(:all,  
:order =\> “lft, depth, name”  
).map{|category|  
["-" \* category.depth +  
category.name,  
category.id]}

Note: you will have to tweak this depending on the schema.

I would also push this to a method of the Model to clean up you  
Controller/View and make it available to other Controllers.

---

<div class="post-metadata">

**Author:** ![marek](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/b2d939/32.png) [@marek](https://www.ruby-forum.com/u/marek)\
**Post date:** [January 16, 2007, 2:21pm UTC](https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810/5 "2007-01-16T14:21:17Z")

</div>

Andrew S. wrote:

> Get the database to do it for you:
> 
> Category.find(:all,  
> :order =\> “lft, depth, name”  
> ).map{|category|  
> ["-" \* category.depth +  
> category.name,  
> category.id]}

The result it’s the same ☹  
i think because it first order all by lft (that is a unique value), and  
then by other (but never happen because there aren’t lft equals)… ☹

---

<div class="post-metadata">

**Author:** ![marek](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/b2d939/32.png) [@marek](https://www.ruby-forum.com/u/marek)\
**Post date:** [January 17, 2007, 9:13am UTC](https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810/6 "2007-01-17T09:13:19Z")

</div>

Andrew S. wrote:

> Yep - that would kill it. Let’s try again - Can you post an example  
> output from:
> 
> Category.find(:all, :order =\> “lft”)

[[“root”, 4], ["-contry", 5], ["–andorra", 7], ["–norway", 11],  
["–belgium", 12], ["-food",14], ["–thai", 15], ["–english", 18],  
[“Yorkshire Pudding”, 19], [“Fish and chips”, 20]]  
this is what i get now 🙂  
i’m thinking about that maybe it’s also possible order them when i  
insert it into the db (with add\_child), so i’ll have in the db something  
ordered and with right lft, so i can just find(:all), without order them  
on each request

---

<div class="post-metadata">

**Author:** ![marek](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/b2d939/32.png) [@marek](https://www.ruby-forum.com/u/marek)\
**Post date:** [January 17, 2007, 9:26am UTC](https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810/7 "2007-01-17T09:26:02Z")

</div>

marek wrote:

> Andrew S. wrote:
> 
> > Yep - that would kill it. Let’s try again - Can you post an example  
> > output from:
> > 
> > Category.find(:all, :order =\> “lft”)
> 
> [[“root”, 4], ["-contry", 5], ["–andorra", 7], ["–norway", 11],  
> ["–belgium", 12], ["-food",14], ["–thai", 15], ["–english", 18],  
> [“Yorkshire Pudding”, 19], [“Fish and chips”, 20]]  
> this is what i get now 🙂  
> i’m thinking about that maybe it’s also possible order them when i  
> insert it into the db (with add\_child), so i’ll have in the db something  
> ordered and with right lft, so i can just find(:all), without order them  
> on each request

ops, forget something the right one is (directly from the console):  
[[“root”, 4], ["-contry", 5], ["–andorra", 7], ["–norway", 11],  
["–belgium", 12], ["-food",14], ["–thai", 15], ["–english", 18],  
["—Yorkshire Pudding", 19], ["—Fish and chips", 20]]

---

<div class="post-metadata">

**Author:** ![marek](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/b2d939/32.png) [@marek](https://www.ruby-forum.com/u/marek)\
**Post date:** [January 17, 2007, 1:45am UTC](https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810/8 "2007-01-17T01:45:35Z")

</div>

marek wrote:

> i think because it first order all by lft (that is a unique value), and  
> then by other (but never happen because there aren’t lft equals)… ☹

Yep - that would kill it. Let’s try again - Can you post an example  
output from:

Category.find(:all, :order =\> “lft”)

---

<div class="post-metadata">

**Author:** ![marek](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/b2d939/32.png) [@marek](https://www.ruby-forum.com/u/marek)\
**Post date:** [January 18, 2007, 5:22pm UTC](https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810/9 "2007-01-18T17:22:45Z")

</div>

Andrew S. wrote:

> How about just :
> 
> Category.find(:all, :order =\> “depth, name”)  
> .map{|category| ["-" \* category.depth + category.name, category.id]}
> 
> Something that still is not clear to me - are the “-”'s stored in the  
> database? The console result you pasted suggests yes, but the code  
> above seems to be adding it.
> 
> There must be a way for the database to do this for us, but without the  
> schema is is difficult to build the correct SQL query.

it doesn’t work because it order all by depth and the by name, so it  
lost the hierarchy…the result is:  
[[“root”, 4], ["-contry", 5], ["-food",14], ["–andorra", 7],  
["–belgium", 12], ["–english", 18], ["–norway", 11], ["–thai", 15],  
["—Fish and chips", 20], ["—Yorkshire Pudding", 19]]  
that isn’t right

the “-” aren’t stored in the db, they’re calculated in the map

---

<div class="post-metadata">

**Author:** ![marek](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/b2d939/32.png) [@marek](https://www.ruby-forum.com/u/marek)\
**Post date:** [January 18, 2007, 6:17pm UTC](https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810/10 "2007-01-18T18:17:05Z")

</div>

Hey,

the problem everyone seems to be having here is forgetting that this  
is a recursive data structure.

Assuming each category has a parent\_id field, and assuming that root  
categories have a parent\_id which is nil, this should work:

in category.rb:

def self.options\_for\_select(records = nil)  
records ||= find(:all, :order =\> ‘name ASC’)  
records\_by\_parent = records.group\_by(&:parent\_id)

collector = lambda do |memo, category|  
memo \<\< [’\*’ \* category.depth + category.name, category.id]  
Array(records\_by\_parent[category.id]).inject(memo, &collector)  
end

records\_by\_parent[nil].inject([], &collector)  
end

Regards,  
Trevor

---

<div class="post-metadata">

**Author:** ![marek](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/b2d939/32.png) [@marek](https://www.ruby-forum.com/u/marek)\
**Post date:** [January 18, 2007, 3:52am UTC](https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810/11 "2007-01-18T03:52:27Z")

</div>

How about just :

Category.find(:all, :order =\> “depth, name”)  
.map{|category| ["-" \* category.depth + category.name, category.id]}

Something that still is not clear to me - are the “-”'s stored in the  
database? The console result you pasted suggests yes, but the code  
above seems to be adding it.

There must be a way for the database to do this for us, but without the  
schema is is difficult to build the correct SQL query.

---

<div class="post-metadata">

**Author:** ![marek](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/b2d939/32.png) [@marek](https://www.ruby-forum.com/u/marek)\
**Post date:** [January 18, 2007, 9:35pm UTC](https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810/12 "2007-01-18T21:35:44Z")

</div>

Trevor S. wrote:

> def self.options\_for\_select(records = nil)  
> records ||= find(:all, :order =\> ‘name ASC’)  
> records\_by\_parent = records.group\_by(&:parent\_id)
> 
> collector = lambda do |memo, category|  
> memo \<\< [’\*’ \* category.depth + category.name, category.id]  
> Array(records\_by\_parent[category.id]).inject(memo, &collector)  
> end
> 
> records\_by\_parent[nil].inject([], &collector)  
> end

Perfect 🙂  
but just one thing, about performance…if there’re a lot of people that  
are loading the page i think it’ll be a problem, what do you think?  
i think that it’s better order them when are inserted into the db (also  
because it’s done just once, or eventually not so much frequently), so  
it’s possible to get all the list order just by one select and no  
operations, instead of order all for each request

---

<div class="post-metadata">

**Author:** ![marek](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/b2d939/32.png) [@marek](https://www.ruby-forum.com/u/marek)\
**Post date:** [January 19, 2007, 1:49am UTC](https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810/13 "2007-01-19T01:49:59Z")

</div>

Trevor S. wrote:

> Hey,
> 
> the problem everyone seems to be having here is forgetting that this  
> is a recursive data structure.
> 
> Assuming each category has a parent\_id field, and assuming that root  
> categories have a parent\_id which is nil, this should work:
> 
> in category.rb:
> 
> def self.options\_for\_select(records = nil)  
> records ||= find(:all, :order =\> ‘name ASC’)  
> records\_by\_parent = records.group\_by(&:parent\_id)
> 
> collector = lambda do |memo, category|  
> memo \<\< [’\*’ \* category.depth + category.name, category.id]  
> Array(records\_by\_parent[category.id]).inject(memo, &collector)  
> end
> 
> records\_by\_parent[nil].inject([], &collector)  
> end
> 
> Regards,  
> Trevor

Nice! While I can follow the code, there are a few concepts I have  
never seen before. Every time I think I get it, I lose it again.

In time I will master this ruby-foo move - thanks for expanding my  
knowledge 🙂

---

<div class="post-metadata">

**Author:** ![marek](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/b2d939/32.png) [@marek](https://www.ruby-forum.com/u/marek)\
**Post date:** [January 18, 2007, 9:07pm UTC](https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810/14 "2007-01-18T21:07:15Z")

</div>

daniel wrote:

> it doesn’t work because it order all by depth and the by name, so it  
> lost the hierarchy…the result is:  
> [[“root”, 4], ["-contry", 5], ["-food",14], ["–andorra", 7],  
> ["–belgium", 12], ["–english", 18], ["–norway", 11], ["–thai", 15],  
> ["—Fish and chips", 20], ["—Yorkshire Pudding", 19]]  
> that isn’t right
> 
> the “-” aren’t stored in the db, they’re calculated in the map

as daniel said… 🙂

thanks trevor, i’ll try… root parent\_id is 0, but i think it’s not a  
problem change it to nil 🙂

---

<div class="post-metadata">

**Author:** ![marek](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/b2d939/32.png) [@marek](https://www.ruby-forum.com/u/marek)\
**Post date:** [January 19, 2007, 4:31pm UTC](https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810/15 "2007-01-19T16:31:05Z")

</div>

Hey,

if the parent\_id for your root(s) is 0 then you don’t have to alter  
records in your database, just change the last line of the method I  
gave you to this:

records\_by\_parent[0].inject([], &collector)

Trevor

---

<div class="post-metadata">

**Author:** ![marek](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/b2d939/32.png) [@marek](https://www.ruby-forum.com/u/marek)\
**Post date:** [January 19, 2007, 4:31pm UTC](https://www.ruby-forum.com/t/is-possible-to-order-a-nested-model/81810/16 "2007-01-19T16:31:15Z")

</div>

Hey,

see below:

On 18-Jan-07, at 12:35 PM, marek wrote:

> > end
> 
> Perfect 🙂  
> but just one thing, about performance…if there’re a lot of people  
> that are loading the page i think it’ll be a problem, what do you  
> think?  
> i think that it’s better order them when are inserted into the db  
> (also because it’s done just once, or eventually not so much  
> frequently), so it’s possible to get all the list order just by one  
> select and no operations, instead of order all for each request

I prefer not to speculate on whether it’ll be a performance ‘problem’

- performance is something that can really only be determined by  
measuring on relevant hardware with relevant tests.

There’s often knee-jerk reactions to your question (which are often  
right) such as “use fragment caching”. However, my attitude is: if  
you can figure out a way to hyper-optimize it today, you can  
certainly figure out how to hyper-optimize the day a real issue  
emerges to prove _this_ is the best place to spend your time trying  
to speed things up.

If it works and it’s not a screaming obvious performance issue (like  
10 second page load times) then just move on, you’re done and you’ve  
probably got better things to do.

Oh, and if you really can’t let it go and you just _have_ to try and  
optimize this… then just “use fragment caching” 🙂

Trevor
