Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

sorting by quarterly calendar

by data67 (Monk)
on Aug 27, 2001 at 18:27 UTC ( #108133=perlquestion: print w/replies, xml ) Need Help??

data67 has asked for the wisdom of the Perl Monks concerning the following question:

I am looking for a good way to sort a report that I have based on a QUARTERLY calendar (where 1st quarter = jun, jul, aug. 2nd quarter = sep, oct, nov. 3rd quarter = dec, jan, feb. 4th quarter = mar, apr, may). The current date format is "example: 10/29/2001", i am trying to sort based on these dates and put the result in a format where my report is ordered quarterly (i.e., q1, q2, q3, q4) and a final part on the end where the report lists entries without specified dates. Any suggestions will be greatly appreciated.

Replies are listed 'Best First'.
Re: sorting by quarterly calendar
by VSarkiss (Monsignor) on Aug 27, 2001 at 18:42 UTC

    Well, an easy way to do it would be to map the month to a quarter with an array:

    my @quarter = qw(q0 q3 q3 q4 q4 q4 q1 q1 q1 q2 q2 q2 q3); # Later... @sorted = sort { $quarter[(split(/\//, $a))[0]] cmp $quarter[(split(/\//, $b))[0]] } @rawdates;
    Just to clarify, the split breaks up the date strings into components at forward slashes, and the [0] pulls out the month part.

    Note: I'm assuming all the dates are in the same year, although it shouldn't be hard to extend this to sort on year also.

    HTH

(Ovid) Re: sorting by quarterly calendar
by Ovid (Cardinal) on Aug 27, 2001 at 20:12 UTC

    I think the following would work. I didn't try to adjust for null dates as I have no idea what your data format is. The snippet sorts by year, then by quarter, then by day.

    use strict; use warnings; use Data::Dumper; my @sorted = map { [ $_->[0], join( '/', @{$_}[1..3] ), $_->[4] ] } sort { $a->[3] <=> $b->[3] || $a->[0] <=> $b->[0] || $a-> +[2] <=> $b->[2] } map { [ get_quarter( $_->[0] ), split( '/', $_->[0] ), $ +_->[1] ] } map { [ split( /\s/, $_, 2 ) ] } <DATA>; chomp @$_ foreach @sorted; print Dumper \@sorted; sub get_quarter { my @quarters = qw( 3 3 4 4 4 1 1 1 2 2 2 3 ); my ( $month ) = (split'/',$_[0])[0]; $quarters[ $month - 1 ]; } __DATA__ 10/23/2000 data1 foo bar baz 11/19/2000 data2 some stuff 08/11/2001 data3 what the heck? 04/30/2001 data4 this is getting dull 01/01/2001 data5 ??

    Here's what it does:

    • It splits the data on whitespace. That leaves the date in the first field. Note the optional third parameter to split which ensures that all data after the date is left untouched.
    • It passes the first field to the sub get_quarter() and returns the quarter, along with the date split on the delimeter and the remaining data.
    • It sorts on year, then quarter, then day.
    • It puts the data back together.

    Each array ref is going to have the quarter as the first element, the date as the second and the data as the third.

    You're going to have to play with the above snippet as you need to account for null dates. Further, I really didn't test this for multiple quarters spanning multiple years (I assumed you'd have one fiscal year of data at a time).

    Cheers,
    Ovid

    Vote for paco!

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re: sorting by quarterly calendar
by cLive ;-) (Prior) on Aug 27, 2001 at 21:55 UTC
    Since not specified, let's assume you throw in one year's worth of reports at a time, and that you can parse the reports into a hash of hashes, so that:
    #!/usr/bin/perl use strict; # reports to sort - you will need to parse existing to build HoH my %report; # dummy data $report{'report_1'}{'date'} = '10/29/2001'; $report{'report_1'}{'content'} = 'whatever'; $report{'report_2'}{'date'} = '12/29/2001'; $report{'report_2'}{'content'} = 'whatever2'; $report{'report_3'}{'date'} = '4/29/2001'; $report{'report_3'}{'content'} = 'whatever3'; $report{'report_4'}{'date'} = ''; $report{'report_4'}{'content'} = 'whatever4'; # define quarters my %quarter = (1 => 'q3', 2 => 'q3', 3 => 'q4', 4 => 'q4', 5 => 'q4', 6 => 'q1', 7 => 'q1', 8 => 'q1', 9 => 'q2', 10 => 'q2', 11 => 'q2', 12 => 'q3'); my %sorted_reports; for (keys %report) { # grab month if ($report{$_}{'date'} =~ m|^(\d+)|) { # work out quarter # force number - covers 01/1 inconsistancy my $q = $quarter{$1+0}; # whack into hash of arrays push @{$sorted_reports{$q}}, $report{$_}{'content'}; } else { # no date push @{$sorted_reports{'undated reports'}}, $report{$_}{'content'} +; } } # now print (or whatever) # note can sort because 'u' follows 'q' foreach my $quarter (sort { $a cmp $b; } keys %sorted_reports) { # print quarter print "\n\n$quarter:\n\n"; # and reports print join "\n\n----------\n\n", @{$sorted_reports{$quarter}}; print "\n\n", '=' x 60; }
    cLive ;-)
Re: sorting by quarterly calendar
by damian1301 (Curate) on Aug 27, 2001 at 21:36 UTC
(dws)Re: sorting by quarterly calendar
by dws (Chancellor) on Aug 27, 2001 at 22:13 UTC
    I am looking for a good way to sort a report that I have based on a QUARTERLY calendar...

    I wonder if you may be reaching for the wrong hammer. Unless you have some requirements that aren't stated, the problem you face is how to find "breaks" in a sorted list of calendar entries, where the breaks correspond to changes in quarter.

    Sorting by date is easy. There are some pointers in this thread that'll get you there.

    Given the date format you're using (month/day/year), spotting breaks in a sorted list is also easy: Process the list in order. For each entry, extract the month and use it as a key to lookup the quarter in an array or hash, and do your break whenever the quarter changes. The basics of that technique are touched on in Vsarkiss's and CLive ;-)'s posts above.

Re: sorting by quarterly calendar
by mandog (Curate) on Aug 27, 2001 at 23:18 UTC

    The code below is less elegant than others in this thread. I understand qw// better now...

    However, I do attempt to use the supplied sample data, account for 1 or 2 digit months and days and puke if I don't get a 4 digit year.

    #!/usr/bin/perl -w use strict; my @test=( "example: 10/29/2001", "example: 2/18/2000 3rd quarter", "example: 3/18/2000 4th quarter", "example: 4/18/2000 4th quarter", "example: 5/18/2000 4th quarter", "example: 6/18/2000 1st quarter", "example: 7/18/2000 1st quarter", "example: 11/18/2001 2nd quarter", "example: 01/18/1999 3rd quarter", "example: 02/18/1999 3rd quarter", "example: 3/18/1999 4th quarter", "example: 4/18/1999 4th quarter", "example: 5/18/1999 4th quarter", "example: 6/18/1999 1 quarter", "example: 7/18/1999 1 quarter", "example: 8/18/1999 1 quarter", "example: 9/18/1999 2nd quarter", "example: 10/18/1999 2nd quarter", "example: 11/18/1999 2nd quarter", "example: 12/18/1999 3rd quarter" ); sub my_compare(){ # use "our" to limit scope to function but persist # map months to quarters our %quarters=( '6'=> 1, '06'=> 1, '7'=> 1, '07'=> 1, '8'=> 1, '08'=> 1, '9'=> 2, '09'=> 2, '10'=> 2, '11'=> 2, '12'=> 3, '1'=> 3, '01'=> 3, '2'=> 3, '02'=> 3, '3'=> 4, '03'=> 4, '4'=> 4, '04'=> 4, '5'=> 4, '05'=> 4 ); # $a and $b are given to us by sort # they are the left & right sides of the comparision # my ($mon, $day); $a=~m|(\d*)/(\d*)/(\d*)|; $mon=length($1)<2?'0'.$1:$1; $day=length($2)<2?'0'.$2:$2; my $a3=$3 . $quarters{$mon}.$mon .$day; die "bad year in $a" unless length ($3)==4; $b=~m|(\d*)/(\d*)/(\d*)|; $mon=length($1)<2?'0'.$1:$1; $day=length($2)<2?'0'.$2:$2; my $b3=$3 . $quarters{$mon}. $mon .$day; die "bad year in $b" unless length ($3)==4; return $a3 <=> $b3; } @test=sort(my_compare(), @test); print join "\n", @test;

    --mandog

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://108133]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (2)
As of 2022-05-18 19:51 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Do you prefer to work remotely?



    Results (71 votes). Check out past polls.

    Notices?