Two stage array sorting

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 :slight_smile:

Ideas?

Thanks much...

···

--
Posted via http://www.ruby-forum.com/.

Tim Ferrell 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 :slight_smile:

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
marnen@marnen.org
--
Posted via http://www.ruby-forum.com/.

Morning Tim,

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 :slight_smile:

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

···

On Fri, Oct 30, 2009 at 9:35 AM, Tim Ferrell <s0nspark@gmail.com> wrote:

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

···

On Oct 30, 10:35 am, Tim Ferrell <s0nsp...@gmail.com> 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?

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

--
Posted via http://www.ruby-forum.com/.

John W Higgins wrote:

Morning Tim,

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 :slight_smile:

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

···

On Fri, Oct 30, 2009 at 9:35 AM, Tim Ferrell <s0nspark@gmail.com> wrote:

--
Posted via http://www.ruby-forum.com/.

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)

···

On Oct 30, 11:17 am, John W Higgins <wish...@gmail.com> wrote:

[Note: parts of this message were removed to make it a legal post.]

Morning Tim,

On Fri, Oct 30, 2009 at 9:35 AM, Tim Ferrell <s0nsp...@gmail.com> 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 :slight_smile:

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