# Two stage array sorting

**URL:** <https://www.ruby-forum.com/t/two-stage-array-sorting/178197>\
**Category:** Ruby\
**Created:** [October 30, 2009, 5:34pm UTC](https://www.ruby-forum.com/t/two-stage-array-sorting/178197 "2009-10-30T17:34:57Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![s0nspark](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/f475e1/32.png) [@s0nspark](https://www.ruby-forum.com/u/s0nspark)\
**Post date:** [October 30, 2009, 5:34pm UTC](https://www.ruby-forum.com/t/two-stage-array-sorting/178197/1 "2009-10-30T17:34:57Z")

</div>

I have an array of strings like so:

Tuesday 12:00 AM  
Sunday 9:00 AM  
Tuesday 3:00 PM  
Friday 7:30 AM  
Wednesday 5:00 PM

and I want the array sorted by day name and then time. Is it possible to  
do this without resorting to a two stage process? Currently I am  
breaking the array out into arrays by day and sorting those by time and  
then reassembling… it just feels dirty 🙂

Ideas?

Thanks much…

---

<div class="post-metadata">

**Author:** ![s0nspark](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/f475e1/32.png) [@s0nspark](https://www.ruby-forum.com/u/s0nspark)\
**Post date:** [October 30, 2009, 6:12pm UTC](https://www.ruby-forum.com/t/two-stage-array-sorting/178197/2 "2009-10-30T18:12:18Z")

</div>

Tim F. wrote:

> I have an array of strings like so:
> 
> Tuesday 12:00 AM  
> Sunday 9:00 AM  
> Tuesday 3:00 PM  
> Friday 7:30 AM  
> Wednesday 5:00 PM
> 
> and I want the array sorted by day name and then time. Is it possible to  
> do this without resorting to a two stage process? Currently I am  
> breaking the array out into arrays by day and sorting those by time and  
> then reassembling… it just feels dirty 🙂
> 
> Ideas?

array.sort\_by{|s| s.split(’ ')}

Of couse, that will do alphabetic sorting in each half. If you need  
time sorting, my suggestion would be to use a date parsing function in  
the sort\_by block.

> Thanks much…

## Best,

Marnen Laibow-Koser  
[http://www.marnen.org](http://www.marnen.org)  
removed\_email\_address@domain.invalid

---

<div class="post-metadata">

**Author:** ![s0nspark](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/f475e1/32.png) [@s0nspark](https://www.ruby-forum.com/u/s0nspark)\
**Post date:** [October 30, 2009, 6:19pm UTC](https://www.ruby-forum.com/t/two-stage-array-sorting/178197/3 "2009-10-30T18:19:25Z")

</div>

Morning Tim,

On Fri, Oct 30, 2009 at 9:35 AM, Tim F. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> do this without resorting to a two stage process? Currently I am  
> breaking the array out into arrays by day and sorting those by time and  
> then reassembling… it just feels dirty 🙂

require ‘date’

def sort\_datetime\_array(ary)  
fmt = ‘%A %I:%M %p’  
ary.sort{ |a,b|  
DateTime.strptime(a, fmt) \<=\> DateTime.strptime(b, fmt)  
}  
end

c = [‘Friday 1:00 PM’, ‘Tuesday 12:00 AM’, ‘Wednesday 3:00 PM’, ‘Tuesday  
11:59 PM’]  
puts sort\_datetime\_array(c)

–output–  
Tuesday 12:00 AM  
Tuesday 11:59 PM  
Wednesday 3:00 PM  
Friday 1:00 PM

Just note that the original array is not sorted in this case - the  
method  
returns a new sorted array - if you wanted the sorting done in place  
then  
change sort to sort! in the method

John

---

<div class="post-metadata">

**Author:** ![s0nspark](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/f475e1/32.png) [@s0nspark](https://www.ruby-forum.com/u/s0nspark)\
**Post date:** [October 30, 2009, 6:44pm UTC](https://www.ruby-forum.com/t/two-stage-array-sorting/178197/4 "2009-10-30T18:44:27Z")

</div>

Perfect … thanks so much!

Cheers,  
Tim

John W Higgins wrote:

> require ‘date’
> 
> def sort\_datetime\_array(ary)  
> fmt = ‘%A %I:%M %p’  
> ary.sort{ |a,b|  
> DateTime.strptime(a, fmt) \<=\> DateTime.strptime(b, fmt)  
> }  
> end

---

<div class="post-metadata">

**Author:** ![s0nspark](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/f475e1/32.png) [@s0nspark](https://www.ruby-forum.com/u/s0nspark)\
**Post date:** [October 30, 2009, 11:59pm UTC](https://www.ruby-forum.com/t/two-stage-array-sorting/178197/5 "2009-10-30T23:59:11Z")

</div>

John W Higgins wrote:

> Morning Tim,
> 
> On Fri, Oct 30, 2009 at 9:35 AM, Tim F. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:
> 
> > do this without resorting to a two stage process? Currently I am  
> > breaking the array out into arrays by day and sorting those by time and  
> > then reassembling… it just feels dirty 🙂
> 
> require ‘date’
> 
> def sort\_datetime\_array(ary)  
> fmt = ‘%A %I:%M %p’  
> ary.sort{ |a,b|  
> DateTime.strptime(a, fmt) \<=\> DateTime.strptime(b, fmt)  
> }  
> end
> 
> c = [‘Friday 1:00 PM’, ‘Tuesday 12:00 AM’, ‘Wednesday 3:00 PM’, ‘Tuesday  
> 11:59 PM’]  
> puts sort\_datetime\_array(c)
> 
> –output–  
> Tuesday 12:00 AM  
> Tuesday 11:59 PM  
> Wednesday 3:00 PM  
> Friday 1:00 PM
> 
> Just note that the original array is not sorted in this case - the  
> method  
> returns a new sorted array - if you wanted the sorting done in place  
> then  
> change sort to sort! in the method
> 
> John

In this case, sort\_by is faster because it creates less DateTime  
objects.

def sort\_by\_datetime(ary)  
ary.sort\_by {|el| Date.strptime(el, ‘%A %I:%M %p’) }  
end

(see the ri for enumerable#sort\_by ).

Hth,

Siep

---

<div class="post-metadata">

**Author:** ![s0nspark](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/f475e1/32.png) [@s0nspark](https://www.ruby-forum.com/u/s0nspark)\
**Post date:** [October 31, 2009, 12:10am UTC](https://www.ruby-forum.com/t/two-stage-array-sorting/178197/6 "2009-10-31T00:10:46Z")

</div>

On Oct 30, 10:35 am, Tim F. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> I have an array of strings like so:
> 
> Tuesday 12:00 AM  
> Sunday 9:00 AM  
> Tuesday 3:00 PM  
> Friday 7:30 AM  
> Wednesday 5:00 PM
> 
> and I want the array sorted by day name and then time. Is it possible to  
> do this without resorting to a two stage process?

Although you already have better answers specific to your needs, the  
answer to this question is also yes:  
class Array; def rand; self[Kernel.rand(length)]; end; end  
Person = Struct.new( :first, :last, :age ) do  
def to\_s; “#{last}, #{first} - #{age}”; end  
end  
FIRSTS = %w[Gavin Lisa John Joe Jim Jill Bob Anne Zach Michael]  
LASTS = %w[Jones Smith Jackson]  
people = (0…40).map{ Person.new( FIRSTS.rand, LASTS.rand, rand  
(60) ) }

# Name ascending, age descending

puts people.sort\_by{ |who| [who.last, who.first, -who.age] }

#=\> Jackson, Anne - 28  
#=\> Jackson, Anne - 22  
#=\> Jackson, Bob - 29  
#=\> Jackson, Bob - 9  
#=\> Jackson, Gavin - 54  
#=\> Jackson, Jim - 12  
#=\> Jackson, Jim - 9  
#=\> Jackson, Jim - 1  
#=\> Jackson, Joe - 47  
#=\> Jackson, John - 41  
#=\> Jackson, Lisa - 8  
#=\> Jackson, Zach - 38  
#=\> Jones, Anne - 52  
#=\> Jones, Bob - 48  
#=\> Jones, Bob - 15  
#=\> Jones, Bob - 6  
#=\> Jones, Gavin - 30  
#=\> Jones, Jill - 35  
#=\> Jones, Jim - 50  
#=\> Jones, Jim - 49  
#=\> Jones, Jim - 44  
#=\> Jones, Jim - 3  
#=\> Jones, Joe - 35  
#=\> Jones, Joe - 11  
#=\> Jones, Joe - 4  
#=\> Jones, John - 37  
#=\> Jones, John - 10  
#=\> Jones, Zach - 21  
#=\> Jones, Zach - 2  
#=\> Smith, Anne - 20  
#=\> Smith, Bob - 48  
#=\> Smith, Gavin - 4  
#=\> Smith, Jill - 56  
#=\> Smith, Jill - 39  
#=\> Smith, Jill - 25  
#=\> Smith, Jill - 2  
#=\> Smith, Jim - 7  
#=\> Smith, Joe - 49  
#=\> Smith, John - 44  
#=\> Smith, John - 29  
#=\> Smith, John - 28

---

<div class="post-metadata">

**Author:** ![s0nspark](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/f475e1/32.png) [@s0nspark](https://www.ruby-forum.com/u/s0nspark)\
**Post date:** [October 31, 2009, 12:08am UTC](https://www.ruby-forum.com/t/two-stage-array-sorting/178197/7 "2009-10-31T00:08:25Z")

</div>

On Oct 30, 11:17 am, John W Higgins [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> > I have an array of strings like so:  
> > then reassembling… it just feels dirty 🙂
> 
> require ‘date’
> 
> def sort\_datetime\_array(ary)  
> fmt = ‘%A %I:%M %p’  
> ary.sort{ |a,b|  
> DateTime.strptime(a, fmt) \<=\> DateTime.strptime(b, fmt)  
> }  
> end

Note that using sort is much slower than using sort\_by (though perhaps  
slightly more memory-efficient). This is possibly only significant if  
you’re sorting large amount of items, however. I mostly prefer sort\_by  
because it is DRYer (and hence less typing :).

require ‘date’  
require ‘benchmark’

class Array; def rand; self[Kernel.rand(length)]; end; end  
DAYS = %w[Monday Tuesday Wednesday Thursday Friday Saturday Sunday]  
AMPM = %w[AM PM]  
FORMAT = ‘%A %I:%M %p’

Benchmark.bmbm do |x|  
[10,100,1\_000,10\_000].each do |n|  
a = (0…n).map{ “#{DAYS.rand} #{rand(12)+1}:#{rand(60)} #  
{AMPM.rand}” }  
x.report( “sort #{n}”) do  
a.sort{ |y,z| Date.strptime(y,FORMAT) \<=\> Date.strptime  
(z,FORMAT) }  
end  
x.report( “sort\_by #{n}”) do  
a.sort\_by{ |y| Date.strptime(y,FORMAT) }  
end  
end  
end

#=\> Rehearsal -------------------------------------------------  
#=\> sort 10 0.020000 0.000000 0.020000 ( 0.020270)  
#=\> sort\_by 10 0.000000 0.000000 0.000000 ( 0.003464)  
#=\> sort 100 0.200000 0.000000 0.200000 ( 0.204563)  
#=\> sort\_by 100 0.030000 0.000000 0.030000 ( 0.032382)  
#=\> sort 1000 2.390000 0.040000 2.430000 ( 2.455060)  
#=\> sort\_by 1000 0.300000 0.010000 0.310000 ( 0.316943)  
#=\> sort 10000 19.700000 0.280000 19.980000 ( 20.197492)  
#=\> sort\_by 10000 3.270000 0.060000 3.330000 ( 3.361712)  
#=\> --------------------------------------- total: 26.300000sec  
#=\> user system total real  
#=\> sort 10 0.020000 0.000000 0.020000 ( 0.016442)  
#=\> sort\_by 10 0.000000 0.000000 0.000000 ( 0.003143)  
#=\> sort 100 0.190000 0.000000 0.190000 ( 0.190505)  
#=\> sort\_by 100 0.030000 0.000000 0.030000 ( 0.026150)  
#=\> sort 1000 2.300000 0.030000 2.330000 ( 2.351752)  
#=\> sort\_by 1000 0.290000 0.010000 0.300000 ( 0.298472)  
#=\> sort 10000 19.020000 0.240000 19.260000 ( 19.437129)  
#=\> sort\_by 10000 3.310000 0.050000 3.360000 ( 3.389367)
