[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Sorting Dates and Times in an array

Paul

3/19/2007 4:21:00 PM

Hi there. I am having a bit of trouble trying to solve a particular
sorting problem with an array of data.

Please bear with me for a moment regarding the setup of this problem.
I don't currently have any control over the input data. I am just
trying to format the report of this data as per the requirement.

I have the following sample array data to work with:
@date_array = [['2/22/07','1:03 pm'],['3/9/07','10:45 pm'],
['3/1/07','1:52 pm'],['3/13/07','2:00 pm'],['2/28/07','10:45 am'],
['3/5/07','4:00 pm'],['2/14/07','5:00 pm'],['3/9/07','10:18 am'],
['3/13/07','11:15 am']]

(The actual array contains more data, but this is good enough to work
on this problem. BTW, date format = mm/dd/yy.)

Requirement:
1) Sort in descending order by Date (i.e. @date_array[x][0] )
2) Sort in ascending order by Time (i.e. @date_array[x][1] )

Elsewhere in the script, I use the following line to sort by a
particular element in a row:
> @fields.sort! { |a,b| b[ x ] <=> a[ x ] } # i.e. descending sort

This works quite nicely for string and numeric fields, but *not* for
Date or Time string fields. As an example, when I try it with the
above data I get the following:
----
puts 'Descending Sort - By Date:'
@date_array.sort! { |a,b| b[ 0 ] <=> a[ 0 ] }
@date_array.each_index do |row|
puts "row # #{row} = " + @date_array[row].join(" : ")
end
----
Descending Sort - By Date:
row # 0 = 3/9/07 : 10:18 am
row # 1 = 3/9/07 : 10:45 pm
row # 2 = 3/5/07 : 4:00 pm
row # 3 = 3/13/07 : 2:00 pm
row # 4 = 3/13/07 : 11:15 am
row # 5 = 3/1/07 : 1:52 pm
row # 6 = 2/28/07 : 10:45 am
row # 7 = 2/22/07 : 1:03 pm
row # 8 = 2/14/07 : 5:00 pm
----

--> Which is a nice string sort again, but not a date sort.

I know I'll have to write a method to deal with these two columns of
data, but I'm not sure where to start. I've tried googling solutions
in both this discussion group and the internet but so far haven't
turned up anything I can use.

Suggestions?

19 Answers

Olivier

3/19/2007 5:05:00 PM

0

Le lundi 19 mars 2007 17:25, Paul a écrit :
> Hi there. I am having a bit of trouble trying to solve a particular
> sorting problem with an array of data.
>
> Please bear with me for a moment regarding the setup of this problem.
> I don't currently have any control over the input data. I am just
> trying to format the report of this data as per the requirement.
>
> I have the following sample array data to work with:
> @date_array = [['2/22/07','1:03 pm'],['3/9/07','10:45 pm'],
> ['3/1/07','1:52 pm'],['3/13/07','2:00 pm'],['2/28/07','10:45 am'],
> ['3/5/07','4:00 pm'],['2/14/07','5:00 pm'],['3/9/07','10:18 am'],
> ['3/13/07','11:15 am']]
>
> (The actual array contains more data, but this is good enough to work
> on this problem. BTW, date format = mm/dd/yy.)
>
> Requirement:
> 1) Sort in descending order by Date (i.e. @date_array[x][0] )
> 2) Sort in ascending order by Time (i.e. @date_array[x][1] )
>
> Elsewhere in the script, I use the following line to sort by a
>
> particular element in a row:
> > @fields.sort! { |a,b| b[ x ] <=> a[ x ] } # i.e. descending sort
>
> This works quite nicely for string and numeric fields, but *not* for
> Date or Time string fields. As an example, when I try it with the
> above data I get the following:
> ----
> puts 'Descending Sort - By Date:'
> @date_array.sort! { |a,b| b[ 0 ] <=> a[ 0 ] }
> @date_array.each_index do |row|
> puts "row # #{row} = " + @date_array[row].join(" : ")
> end
> ----
> Descending Sort - By Date:
> row # 0 = 3/9/07 : 10:18 am
> row # 1 = 3/9/07 : 10:45 pm
> row # 2 = 3/5/07 : 4:00 pm
> row # 3 = 3/13/07 : 2:00 pm
> row # 4 = 3/13/07 : 11:15 am
> row # 5 = 3/1/07 : 1:52 pm
> row # 6 = 2/28/07 : 10:45 am
> row # 7 = 2/22/07 : 1:03 pm
> row # 8 = 2/14/07 : 5:00 pm
> ----
>
> --> Which is a nice string sort again, but not a date sort.
>
> I know I'll have to write a method to deal with these two columns of
> data, but I'm not sure where to start. I've tried googling solutions
> in both this discussion group and the internet but so far haven't
> turned up anything I can use.
>
> Suggestions?


Hi Paul,

You may want to first convert your dates/times to real Time objects, so that
they can be compared.

Here is my solution :

require 'pp'
pp @date_array.sort_by {|ary| ary.map{|elt| Time.parse(elt) } }
[["2/14/07", "5:00 pm"],
["2/22/07", "1:03 pm"],
["2/28/07", "10:45 am"],
["3/1/07", "1:52 pm"],
["3/5/07", "4:00 pm"],
["3/9/07", "10:18 am"],
["3/9/07", "10:45 pm"],
["3/13/07", "11:15 am"],
["3/13/07", "2:00 pm"]]
=> nil

A quick explanation :
@date_array is sorted with sort_by, because we want to sort the array
according to a simple criteria. For the comparison, each element of the array
(ie the date and the time string) is converted to a Time object with
Time#parse, and is kept in an array (this is why I used #map).

Doing so, the original arrays of Strings will be sorted, according to the
order of the Arrays containing the real Time objects. For example on a single
element of your original array :
["2/14/07", "5:00 pm"].map{|el| Time.parse(el)}
=> [Wed Feb 14 00:00:00 +0100 2007, Mon Mar 19 17:00:00 +0100 2007]


Instead of comparing arrays that contains one element for the date and another
element for the time, we could have created only one Time object representing
both :

@date_array.sort_by {|ary| Time.parse("#{ary.first} #{ary.last}") }

Regards.

--
Olivier Renaud

Rob Biedenharn

3/19/2007 5:10:00 PM

0


On Mar 19, 2007, at 12:25 PM, Paul wrote:

> Hi there. I am having a bit of trouble trying to solve a particular
> sorting problem with an array of data.
>
> Please bear with me for a moment regarding the setup of this problem.
> I don't currently have any control over the input data. I am just
> trying to format the report of this data as per the requirement.
>
> I have the following sample array data to work with:
> @date_array = [['2/22/07','1:03 pm'],['3/9/07','10:45 pm'],
> ['3/1/07','1:52 pm'],['3/13/07','2:00 pm'],['2/28/07','10:45 am'],
> ['3/5/07','4:00 pm'],['2/14/07','5:00 pm'],['3/9/07','10:18 am'],
> ['3/13/07','11:15 am']]
>
> (The actual array contains more data, but this is good enough to work
> on this problem. BTW, date format = mm/dd/yy.)
>
> Requirement:
> 1) Sort in descending order by Date (i.e. @date_array[x][0] )
> 2) Sort in ascending order by Time (i.e. @date_array[x][1] )
>
> Elsewhere in the script, I use the following line to sort by a
> particular element in a row:
>> @fields.sort! { |a,b| b[ x ] <=> a[ x ] } # i.e. descending sort
>
> This works quite nicely for string and numeric fields, but *not* for
> Date or Time string fields. As an example, when I try it with the
> above data I get the following:
> ----
> puts 'Descending Sort - By Date:'
> @date_array.sort! { |a,b| b[ 0 ] <=> a[ 0 ] }
> @date_array.each_index do |row|
> puts "row # #{row} = " + @date_array[row].join(" : ")
> end
> ----
> Descending Sort - By Date:
> row # 0 = 3/9/07 : 10:18 am
> row # 1 = 3/9/07 : 10:45 pm
> row # 2 = 3/5/07 : 4:00 pm
> row # 3 = 3/13/07 : 2:00 pm
> row # 4 = 3/13/07 : 11:15 am
> row # 5 = 3/1/07 : 1:52 pm
> row # 6 = 2/28/07 : 10:45 am
> row # 7 = 2/22/07 : 1:03 pm
> row # 8 = 2/14/07 : 5:00 pm
> ----
>
> --> Which is a nice string sort again, but not a date sort.
>
> I know I'll have to write a method to deal with these two columns of
> data, but I'm not sure where to start. I've tried googling solutions
> in both this discussion group and the internet but so far haven't
> turned up anything I can use.
>
> Suggestions?

>> epoch = Time.at(0).utc
=> Thu Jan 01 00:00:00 UTC 1970
>> @date_array.sort_by {|d,t| dp=Date.parse(d,true); tp=Time.parse
(t,epoch); [ Date.today - dp, tp ] }.each {|d,t| puts("%8s %8s" %
[d,t]) }
3/13/07 11:15 am
3/13/07 2:00 pm
3/9/07 10:18 am
3/9/07 10:45 pm
3/5/07 4:00 pm
3/1/07 1:52 pm
2/28/07 10:45 am
2/22/07 1:03 pm
2/14/07 5:00 pm
=> [["3/13/07", "11:15 am"], ["3/13/07", "2:00 pm"], ["3/9/07",
"10:18 am"], ["3/9/07", "10:45 pm"], ["3/5/07", "4:00 pm"],
["3/1/07", "1:52 pm"], ["2/28/07", "10:45 am"], ["2/22/07", "1:03
pm"], ["2/14/07", "5:00 pm"]]

Parse the date with 2-digit year semantics (1969-2068 from the 'true'
arg) and parse the time based on the beginning of the epoch (really
any date would do). The sort is then reversed by date and forward by
time when dates are equal.

The use of .sort_by causes the keys to be processed once and is
better for large Arrays than .sort (and if you need .sort!, just do
@date_array = @date_array.sort_by {...} instead).

-Rob

Rob Biedenharn http://agileconsult...
Rob@AgileConsultingLLC.com



Robert Klemme

3/19/2007 5:21:00 PM

0

On 19.03.2007 18:04, Olivier Renaud wrote:

> Instead of comparing arrays that contains one element for the date and another
> element for the time, we could have created only one Time object representing
> both :
>
> @date_array.sort_by {|ary| Time.parse("#{ary.first} #{ary.last}") }

I think this does not meet the OP's requirements (because of descending
date).

epoch = Date.new(0)
@date_array.sort_by {|dt,tm| [epoch - Date.parse(dt), Time.parse(tm)]}

Substraction with epoch will make date components negative and thus lead
to descending ordering.

Kind regards

robert

Rick DeNatale

3/19/2007 5:25:00 PM

0

On 3/19/07, Paul <tester.paul@gmail.com> wrote:
> Hi there. I am having a bit of trouble trying to solve a particular
> sorting problem with an array of data.
>
> Please bear with me for a moment regarding the setup of this problem.
> I don't currently have any control over the input data. I am just
> trying to format the report of this data as per the requirement.
>
> I have the following sample array data to work with:
> @date_array = [['2/22/07','1:03 pm'],['3/9/07','10:45 pm'],
> ['3/1/07','1:52 pm'],['3/13/07','2:00 pm'],['2/28/07','10:45 am'],
> ['3/5/07','4:00 pm'],['2/14/07','5:00 pm'],['3/9/07','10:18 am'],
> ['3/13/07','11:15 am']]
>
> (The actual array contains more data, but this is good enough to work
> on this problem. BTW, date format = mm/dd/yy.)
>
> Requirement:
> 1) Sort in descending order by Date (i.e. @date_array[x][0] )
> 2) Sort in ascending order by Time (i.e. @date_array[x][1] )

(Standard Library) objects are your friend.

rick@frodo:/public/rubyscripts$ cat datesort.rb
date_array = [['2/22/07','1:03 pm'],['3/9/07','10:45 pm'],
['3/1/07','1:52 pm'],['3/13/07','2:00 pm'],['2/28/07','10:45 am'],
['3/5/07','4:00 pm'],['2/14/07','5:00 pm'],['3/9/07','10:18 am'],
['3/13/07','11:15 am']]

p date_array.sort do |a, b|
ddiff = Date.parse(b[0],true) <=> Date.parse(a[0],true)
ddiff == 0 ? Time.parse(a[0]) <=> Time.parse(b[0]) : ddiff
end

rick@frodo:/public/rubyscripts$ ruby datesort.rb
[["2/14/07", "5:00 pm"], ["2/22/07", "1:03 pm"], ["2/28/07", "10:45
am"], ["3/1/07", "1:52 pm"], ["3/13/07", "11:15 am"], ["3/13/07",
"2:00 pm"], ["3/5/07", "4:00 pm"], ["3/9/07", "10:18 am"], ["3/9/07",
"10:45 pm"]]
rick@frodo:/public/rubyscripts$


--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denh...

IPMS/USA Region 12 Coordinator
http://ipmsr12.denh...

Visit the Project Mercury Wiki Site
http://www.mercuryspace...

Olivier

3/19/2007 5:30:00 PM

0

Le lundi 19 mars 2007 18:25, Robert Klemme a écrit :
> On 19.03.2007 18:04, Olivier Renaud wrote:
> > Instead of comparing arrays that contains one element for the date and
> > another element for the time, we could have created only one Time object
> > representing both :
> >
> > @date_array.sort_by {|ary| Time.parse("#{ary.first} #{ary.last}") }
>
> I think this does not meet the OP's requirements (because of descending
> date).
>
> epoch = Date.new(0)
> @date_array.sort_by {|dt,tm| [epoch - Date.parse(dt), Time.parse(tm)]}
>
> Substraction with epoch will make date components negative and thus lead
> to descending ordering.
>
> Kind regards
>
> robert

Yes you're right, I just kept in mind the instruction "sort the dates !". But
I missed the subtlelty. Thanks.

--
Olivier Renaud

Rick DeNatale

3/19/2007 6:16:00 PM

0

On 3/19/07, Rob Biedenharn <Rob@agileconsultingllc.com> wrote:

> The use of .sort_by causes the keys to be processed once and is
> better for large Arrays than .sort (and if you need .sort!, just do
> @date_array = @date_array.sort_by {...} instead).

Not exactly the same if you have more than one reference to the Array.
A more general equivalent to sort! would be:

@date_array.replace(@date_array.sort_by {...})

Another one of those sometimes subtle differences between variables and objects.
--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denh...

Rob Biedenharn

3/19/2007 6:18:00 PM

0


On Mar 19, 2007, at 1:25 PM, Rick DeNatale wrote:

> On 3/19/07, Paul <tester.paul@gmail.com> wrote:
>> Hi there. I am having a bit of trouble trying to solve a particular
>> sorting problem with an array of data.
>>
>> Please bear with me for a moment regarding the setup of this problem.
>> I don't currently have any control over the input data. I am just
>> trying to format the report of this data as per the requirement.
>>
>> I have the following sample array data to work with:
>> @date_array = [['2/22/07','1:03 pm'],['3/9/07','10:45 pm'],
>> ['3/1/07','1:52 pm'],['3/13/07','2:00 pm'],['2/28/07','10:45 am'],
>> ['3/5/07','4:00 pm'],['2/14/07','5:00 pm'],['3/9/07','10:18 am'],
>> ['3/13/07','11:15 am']]
>>
>> (The actual array contains more data, but this is good enough to work
>> on this problem. BTW, date format = mm/dd/yy.)
>>
>> Requirement:
>> 1) Sort in descending order by Date (i.e. @date_array[x][0] )
>> 2) Sort in ascending order by Time (i.e. @date_array[x][1] )
>
> (Standard Library) objects are your friend.
>
> rick@frodo:/public/rubyscripts$ cat datesort.rb
> date_array = [['2/22/07','1:03 pm'],['3/9/07','10:45 pm'],
> ['3/1/07','1:52 pm'],['3/13/07','2:00 pm'],['2/28/07','10:45 am'],
> ['3/5/07','4:00 pm'],['2/14/07','5:00 pm'],['3/9/07','10:18 am'],
> ['3/13/07','11:15 am']]
>
> p date_array.sort do |a, b|
> ddiff = Date.parse(b[0],true) <=> Date.parse(a[0],true)
> ddiff == 0 ? Time.parse(a[0]) <=> Time.parse(b[0]) : ddiff
> end


I think this was meant to be (note Time.parse(a[ 1 ]) not [0])

p date_array.sort do |a,b|
(Data.parse(b[0],true) <=> Date.parse(a[0],true)).nonzero? ||
Time.parse(a[1]) <=> Time.parse(b[1])
end

> rick@frodo:/public/rubyscripts$ ruby datesort.rb
> [["2/14/07", "5:00 pm"], ["2/22/07", "1:03 pm"], ["2/28/07", "10:45
> am"], ["3/1/07", "1:52 pm"], ["3/13/07", "11:15 am"], ["3/13/07",
> "2:00 pm"], ["3/5/07", "4:00 pm"], ["3/9/07", "10:18 am"], ["3/9/07",
> "10:45 pm"]]
> rick@frodo:/public/rubyscripts$
>
>
> --
> Rick DeNatale
>
> My blog on Ruby
> http://talklikeaduck.denh...
>
> IPMS/USA Region 12 Coordinator
> http://ipmsr12.denh...
>
> Visit the Project Mercury Wiki Site
> http://www.mercuryspace...
>

Rob Biedenharn http://agileconsult...
Rob@AgileConsultingLLC.com
+1 513-295-4739
Skype: rob.biedenharn



Rick DeNatale

3/19/2007 6:40:00 PM

0

On 3/19/07, Rob Biedenharn <Rob@agileconsultingllc.com> wrote:

>
> I think this was meant to be (note Time.parse(a[ 1 ]) not [0])

Right you are.


--
Rick

Paul

3/19/2007 7:57:00 PM

0

On Mar 19, 1:21 pm, Robert Klemme wrote:
> I think this does not meet the OP's requirements (because of descending
> date).
>
> epoch = Date.new(0)
> @date_array.sort_by {|dt,tm| [epoch - Date.parse(dt), Time.parse(tm)]}
>
> Substraction with epoch will make date components negative and thus lead
> to descending ordering.
>

Hello Robert, I tried this but I get the following errors:

irb(main):003:0> epoch = Date.new(0)
ArgumentError: wrong number of arguments (1 for 0)
from (irb):3:in `initialize'
from (irb):3

irb(main):005:0> @date_array.sort_by {|dt,tm| [epoch - Date.parse(dt),
Time.parse(tm)]}
NoMethodError: undefined method `parse' for Date:Class
from (irb):5
from (irb):5:in `sort_by'
from (irb):5

Am I missing a 'require' or something to make your lines work?

Paul

3/19/2007 8:23:00 PM

0

On Mar 19, 2:16 pm, "Rick DeNatale" wrote:
> On 3/19/07, Rob Biedenharn wrote:
>
> > The use of .sort_by causes the keys to be processed once and is
> > better for large Arrays than .sort (and if you need .sort!, just do
> > @date_array = @date_array.sort_by {...} instead).
>
> Not exactly the same if you have more than one reference to the Array.
> A more general equivalent to sort! would be:
>
> @date_array.replace(@date_array.sort_by {...})
>

Okay, I've tried to understand this but I'm not quite getting it yet.
Many of the replies posted here integrate the 'print' and the sorting
function, and while normally I'd applaud the efficiency that's not
what I need. I need to replace the original contents of the array
with the sorted contents. Printing afterwards just let's me confirm
that the sorting worked as expected. (By combining the two steps, I'm
only getting partial solutions.)

I've tried using 'sort_by' but I'm not sure how to apply it to multi-
dimensional arrays. What would be the best way to replace the
contents if the data array is really large?

Should I replace my other sort lines with something other? I noticed
that I cannot just change the 'sort' to 'sort_by' in the following
line:
> @date_array.sort! { |a,b| b[ 7 ] <=> a[ 7 ] }

Please let me know. Thanks.

Paul.