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

I am trying to sort a bunch of files. The file names are all 4 numeric characters long. The first two digits are the month and the second two digits are the year. I would like to sort these files so that they appear with the most recent months of the current year first and then the sorted months of the previous years next. If your wondering why, it is to display a list of newsletters.

Prince99

Too Much is never enough...

Replies are listed 'Best First'.
Re: sorting by month within year
by mirod (Canon) on Mar 01, 2001 at 00:11 UTC

    You don't give us enough information here, 2 years for a year is not enough, unless you are dealing with really, really old newsletters!

    But I will assume you are dealing with recent issues, 1951 to 2051 and use a windowing technique. Note that I cannot go from 1930 to 2030 because I think it is patented (and I should stop reading /.).

    I would advise you to simply rename all the files, that will save you trouble down the road.

    #!/bin/perl -w; use strict; foreach my $file (glob( "files/*")) # put the files dir here { rename( $file, correct( $file)) or die "could not rename $file into " . correct( $file) . ": $!" +; } sub correct { my $date= shift; my ($month, $year)= $date=~ /^(\d\d)(\d\d)/; if( $year > 50) { $year +=1900} else { $year+=2000}; return $year.$month; }

    Then you can just sort file names alphabetically and you will be ok.

    If you don't want to rename the files you can use a merlyn-ian transform, also known outside of PM as the Schwartzian Transform:

    my @sorted_filenames= map { $_->[0] } sort { $a->[1] cmp $b->[1] } map { [$_, correct($_)] } glob( "files/*");
Re: sorting by month within year
by davorg (Chancellor) on Mar 01, 2001 at 01:23 UTC

    Gotta do the Schwartzian Transform version...

    my @list = map { $_->[0] } sort { $_->[1] } map { [$_, substr($_, 2).substr($_, 0, 2)] } <*>;

    Actually, on reflection, that's quite like a Guttman-Rosler transform... let's see...

    my @list = map { substr($_, 5) } sort map { substr($_, 2).substr($_, 0, 2).$_ } <*>;
    --
    <http://www.dave.org.uk>

    "Perl makes the fun jobs fun
    and the boring jobs bearable" - me

Re: sorting by month within year
by arturo (Vicar) on Mar 01, 2001 at 00:23 UTC

    I like much of what mirod has to say above. Let me just offer YAWTDI, which technique may prove useful elsewhere. You want to sort by year first, and if the year is the same between two files, by month. Neat trick:

    my @sorted = sort by_year_and_month @files; sub by_year_and_month { year($a) <=> year($b) or month($a) <=>month($b); } sub month { substr shift, 0, 2; #used to say '0, 1' but chipmunk caught the mi +stake } sub year substr shift, 2, 2; #used to be 2, 3 but see above! }

    How it works: if the first <=> comes out 'even' (==0), the second one kicks in. Otherwise, the value of the first comparison is used.

    This slab o' code is not efficient (does the month and year transforms each time ... ugh!). If I were thinking, I'd put a map in here somewhere, but I'll leave that up to you.

    Props to Effective Perl Programming, from whence I picked up the disjunctive sort.

    Philosophy can be made out of anything. Or less -- Jerry A. Fodor

(tye)Re: sorting by month within year
by tye (Sage) on Mar 01, 2001 at 00:17 UTC
    sort { ($a%100)*100+$a/100 <=> ($b%100)*100+$b/100 } <*>

    Just to be perverse. Though it isn't likely that the list of files will be large enough that trying to be efficient would matter much.

            - tye (but my friends call me "Tye")
Re: sorting by month within year
by thealienz1 (Pilgrim) on Mar 01, 2001 at 00:13 UTC

    The simplest solution, but not the most effective would be to just sort a hash of the month and year... check code.

    #this is assuming you are in the right directory #this is not the most efficient way of getting a list of files foreach (<*.*>) { my($month,$year)=split(/\d{2}/,$_); $year+=1900; #Assuming that year started after 1990 if($year<90){$year+=100;} $NEWSLETTER{$year}{$month} = 0; } foreach $y (sort keys %NEWSLETTER) { foreach $m (sort keys $NEWSLETTER{$y}) { #insert code for what you want to do with the sorted year and mo +nth.... } }

    "The pajamas do not like to eat large carnivore toasters."
    In German: "Die Pyjamas mögen nicht große Tiertoaster essen.
    In Spanish: "Los pijamas no tienen gusto de comer las tostadoras grandes del carnívoro."

Re: sorting by month within year
by dws (Chancellor) on Mar 01, 2001 at 00:28 UTC
    If the file dates span 1999 .. 2000, then you'll have a tiny bit of additional work to do beyond the good suggestions above.

    But why not think out of the box? Can the problem be pushed upstream? Are the filenames a given, or can they be renamed? If they can be renamed, go for YYYYMM (or YYYYYMM if you want to be Y10K compatible :)