# Sorting by two fields

**URL:** <https://www.ruby-forum.com/t/sorting-by-two-fields/100175>\
**Category:** Rails\
**Created:** [July 27, 2006, 3:56pm UTC](https://www.ruby-forum.com/t/sorting-by-two-fields/100175 "2006-07-27T15:56:26Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![quik77](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/q/dec6dc/32.png) [@quik77](https://www.ruby-forum.com/u/quik77)\
**Post date:** [July 27, 2006, 3:56pm UTC](https://www.ruby-forum.com/t/sorting-by-two-fields/100175/1 "2006-07-27T15:56:26Z")

</div>

I have a list of TimeSheet objects from an ealier AR query, which I  
can sort by the full name of the user like so:

@time\_sheet\_entries.sort! { |a,b| a.user.full\_name \<=\> b.user.full\_name  
}

I can sort by the started time like so:

@time\_sheet\_entries.sort! { |a,b| a.start\_time \<=\> b.start\_time }

My question is how do I do a sort so that the list is sorted primarily  
by user.full\_name but also by start\_time? So that all the records for  
Joe Blogs are together but within those rather than being random it is  
then ordered by start\_time?

I know I could do it by putting some SQL on the original AR query but  
I’d like to know how to do it without doing that.

FYI what is happening is:

@time\_sheet\_entries.sort …  
foreach entry in @time\_sheet\_entries  
… print out that line in a table  
end

I know that

@time\_sheet\_entries.sort!{ |a,b| a.user.full\_name \<=\> b.full\_name  
}.sort!{ |a,b| a.start\_time \<=\> b.start\_time }

won’t do the trick because assuming it is even valid Ruby it would  
just end up sorting by start\_time. I thought that I could get a list  
of the users then do a foreach loop just picking up the users then  
iterating over them… (pseudo code so don’t expect this to actually  
work…)

@time\_sheet\_entries.sort …  
foreach user in @time\_sheet\_entries.unique(|x| x.user}  
foreach entry in @time\_sheet\_entries.select(|x| x.user = user}  
… print out that line in a table  
end  
end

but I think that’s quite lame. Surely there is a way to do it in one  
line? Any ideas anyone ? 😛

## -Rob

* * *

“On two occasions, I have been asked [by members of Parliament],  
‘Pray, Mr. Babbage, if you put into the machine wrong figures, will  
the right answers come out?’ I am not able to rightly apprehend the  
kind of confusion of ideas that could provoke such a question.”  
– Charles Babbage (1791-1871)

“98.5% of DNA is considered to be junk DNA with no known purpose.  
Maybe it’s XML tags.” – Anon

[http://www.robhulme.com/](http://www.robhulme.com/)  
[http://robhu.livejournal.com/](http://robhu.livejournal.com/)

---

<div class="post-metadata">

**Author:** ![quik77](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/q/dec6dc/32.png) [@quik77](https://www.ruby-forum.com/u/quik77)\
**Post date:** [August 3, 2006, 2:24pm UTC](https://www.ruby-forum.com/t/sorting-by-two-fields/100175/2 "2006-08-03T14:24:37Z")

</div>

On 7/26/06, Robert H. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> My question is how do I do a sort so that the list is sorted primarily  
> foreach entry in @time\_sheet\_entries  
> of the users then do a foreach loop just picking up the users then
> 
> – Charles Babbage (1791-1871)

You could sort it when you get it from the database.

:order =\> ‘field1, field2’

Alternatively, a pretty convoluted method

tmp = @time\_sheet\_entries.group\_by {&:full\_name}

@time\_sheet\_entries = tmp.keys.inject([]) do |arry, key|  
arry \<\< tmp[key].sort\_by{&:start\_date}  
end

---

<div class="post-metadata">

**Author:** ![quik77](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/q/dec6dc/32.png) [@quik77](https://www.ruby-forum.com/u/quik77)\
**Post date:** [August 3, 2006, 2:24pm UTC](https://www.ruby-forum.com/t/sorting-by-two-fields/100175/3 "2006-08-03T14:24:39Z")

</div>

On Tuesday, July 25, 2006, at 5:48 PM, Robert H. wrote:

> by user.full\_name but also by start\_time? So that all the records for  
> … print out that line in a table  
> iterating over them… (pseudo code so don’t expect this to actually  
> but I think that’s quite lame. Surely there is a way to do it in one
> 
> “98.5% of DNA is considered to be junk DNA with no known purpose.  
> Maybe it’s XML tags.” – Anon
> 
> [http://www.robhulme.com/](http://www.robhulme.com/)  
> [http://robhu.livejournal.com/](http://robhu.livejournal.com/)
> 
> * * *
> 
> Rails mailing list  
> removed\_email\_address@domain.invalid  
> [http://lists.rubyonrails.org/mailman/listinfo/rails](http://lists.rubyonrails.org/mailman/listinfo/rails)

@time\_sheet\_entries.sort! do |a,b|  
n = a.user.full\_name \<=\> b.user.full\_name  
n == 0 ? a.start\_time \<=\> b.start\_time : n  
end

The basic idea here is that the block for sort! should return a -1, 0,  
or 1 for the comparison. Just write code in there that will do that for  
the particular sort you want to do. In this case, if the names are  
equal, it compares the start\_times.

@time\_sheet\_entries = @time\_sheet\_entries.sort\_by {|x|  
“#{x.user.full\_name} #{x.start\_time.to\_i}” }

This may also work. The key here is to add the second field in such a  
way that it sorts properly when displayed as a string. In this case, we  
just convert it to a numerical timestamp.

To sort in decending time order, you could do this…

@time\_sheet\_entries = @time\_sheet\_entries.sort\_by {|x|  
“#{x.user.full\_name} #{(Time.now - x.start\_time).to\_i}” }

Warning: Untested YMMV.

\_Kevin  
[www.sciwerks.com](http://www.sciwerks.com)

---

<div class="post-metadata">

**Author:** ![quik77](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/q/dec6dc/32.png) [@quik77](https://www.ruby-forum.com/u/quik77)\
**Post date:** [August 3, 2006, 2:24pm UTC](https://www.ruby-forum.com/t/sorting-by-two-fields/100175/4 "2006-08-03T14:24:40Z")

</div>

> @time\_sheet\_entries.sort! do |a,b|  
> n = a.user.full\_name \<=\> b.user.full\_name  
> n == 0 ? a.start\_time \<=\> b.start\_time : n  
> end  
> That’s brilliant 🙂 Thanks a lot. It works and more importantly makes  
> sense to me when I read it 🙂

> @time\_sheet\_entries = @time\_sheet\_entries.sort\_by {|x|  
> “#{x.user.full\_name} #{x.start\_time.to\_i}” }  
> I considered this but while 2000 \> 100 isn’t it true that “100” \>  
> “2000”. Going to an extra digit is not going to happen any time soon,  
> but it seemed a bit hacky to me (when I originally considered this  
> solution before my email to the RoR list).

## -Rob

* * *

“On two occasions, I have been asked [by members of Parliament],  
‘Pray, Mr. Babbage, if you put into the machine wrong figures, will  
the right answers come out?’ I am not able to rightly apprehend the  
kind of confusion of ideas that could provoke such a question.”  
– Charles Babbage (1791-1871)

“98.5% of DNA is considered to be junk DNA with no known purpose.  
Maybe it’s XML tags.” – Anon

[http://www.robhulme.com/](http://www.robhulme.com/)

> **[robhu](https://robhu.livejournal.com/)**
>
> robhu - the new blog in LiveJournal. There should be new interesting records soon.

---

<div class="post-metadata">

**Author:** ![quik77](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/q/dec6dc/32.png) [@quik77](https://www.ruby-forum.com/u/quik77)\
**Post date:** [August 3, 2006, 2:24pm UTC](https://www.ruby-forum.com/t/sorting-by-two-fields/100175/5 "2006-08-03T14:24:40Z")

</div>

On Thursday, July 27, 2006, at 11:07 AM, Robert H. wrote:

> “2000”. Going to an extra digit is not going to happen any time soon,  
> – Charles Babbage (1791-1871)
> 
> “98.5% of DNA is considered to be junk DNA with no known purpose.  
> Maybe it’s XML tags.” – Anon
> 
> [http://www.robhulme.com/](http://www.robhulme.com/)  
> [http://robhu.livejournal.com/](http://robhu.livejournal.com/)
> 
> * * *
> 
> Rails mailing list  
> removed\_email\_address@domain.invalid  
> [http://lists.rubyonrails.org/mailman/listinfo/rails](http://lists.rubyonrails.org/mailman/listinfo/rails)

Yeah, it is a bit hacky. However, if you print out the time value as  
the unix timestamp and pad it with leading zeros, it will work.

I prefer the first method myself.

\_Kevin  
[www.sciwerks.com](http://www.sciwerks.com)
