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

Howdy Monks from Texas,
I have a problem and since I ain't the brightest star in the night sky I thought I would ask for a little help. I have a webpage that displays the following data. This is a text representation, not the pretty output of the page:
Transid | Date | Time | PagerID | Message Description 99996 | Feb10 | 11:18 | sundallas_e | dspmmr3 at 02/10/2005 10:18:04 +CPU Utilization is at 99.73% 99995 | Feb10 | 11:17 | hpdallas_e | zcompass at 02/10/2005 09:57:04 + CPU Utilization is at 100%
And so forth, the problem I have is with my sort. At any given time I have 200 messages displaying. I sort numerically descending the array to make sure the list displays numerically with the highest value for the Transid at the top. The problem is that once I reach 99999 and roll-over to 00001, the 00001 event displays at the bottom of the list and of course until 200 events occur after this roll-over the page is out of whack. I keep trying to think of exactly what I need to do with additional code at the sort and try fixing this myself, but I have mental block and can't seem to figure it out. Below is the actual code that performs the sorting. Any and all suggestions would be greatly appreciated.
Thanks, Danny
### Output Loop. Sub Routine to generate Web Output to ActiveWindow sub webpage_output { while(1) { if ( -e $Statfile ){ open(ACTWIN,">$ActiveWindow") || die "Couldn't open fi +le $ActiveWindow - $!\n"; print ACTWIN "Transid|Datestamp|Timestamp|Ticket|Pager +Id|Message|Status\n"; ### ### changed method of opening getting the last files in the directory ### also changed sort order to display on screen ### April 5, 2004 opendir(CacheDir,$Cache) || die "Couldn't open the dir +ectory $Cache: $!\n"; @filelist=grep { /[0-9]{5}/ } readdir(CacheDir); closedir(CacheDir); @sortlist=sort {$b <=> $a} @filelist; for ($i=0;$i<$MsgCount;$i++) { $filename=$sortlist[$i]; if ($filename) { open(ITEM,"$Cache/$filename"); while(<ITEM>){ chomp; if(/TransID=(.*)/){$a=$1;} if(/DATE=(.*)/){$b=$1;} if(/TIME=(.*)/){$c=$1;} if(/TICKET=(.*)/){$d=$1;} if(/PagerID=(.*)/){$e=$1;} if(/Message=(.*)/) { $f=$1; $cont="Y"; next; } if(/Status=(.*)/){$g=$ +1; $cont="";next;} if ($cont eq "Y") { $f=$f.$_; } } close(ITEM); print ACTWIN "$a|$b|$c|$d|$e|$f|$g\n"; } } close(ACTWIN); unlink($Statfile) || die "Couldn't delete $Statfile - +$!\n"; } sleep($naptime); } }

20050110 Edit by ysth: code tags around sample data

20050212 Edit by castaway: Changed title from 'sort a nuts'

Replies are listed 'Best First'.
Re: Sorting through a rollover
by fglock (Vicar) on Feb 10, 2005 at 17:38 UTC

    I learned this from ysth other day:

    use strict; use warnings; my @num = qw( 00002 00003 00001 99999 99998 ); @num = sort { ( $a < 50000 ) <=> ( $b < 50000 ) || $a <=> $b } @num; print "sorted: @num \n"; # sorted: 99998 99999 00001 00002 00003
      if

      my @num = qw( 49998 49999 50000 50001 50002 500003)

      you get

      sorted: 50000 50001 50002 500003 49998 49999

      There's got to be a link between the number shown on the page.

      Update

      How about

      sort { abs( $a - $b ) > 200 ? $b <=> $a : $a <=> $b }


      Y'know I just thought of something - looks like you're reading from a log of some sort

      why would you have to sort at all?

      Is it even necessary? (Just checking)
        Or slightly more compactly (using trinary math instead of the trinary operator):
        (abs($a - $b) <=> $max_spread) * ($b <=> $a)
        where $max_spread is the maximum range you expect your numbers to cover before wrapping. 50_000 might be a better choice than 200.

        Caution: Contents may have been coded under pressure.
Re: Sorting through a rollover
by trammell (Priest) on Feb 10, 2005 at 19:20 UTC
    Add 1000000, sort on that, subtract 1000000.

    Update: man, that is just not right.

      MAN THAT AIN'T RIGHT! How simple, it was staring me in the eyes and I couldn't see the tree for the forest. I was trying to make a mountain out of an ant hill. Thanks a lot, that is the definative answer that requires very little in terms of logic.
      THANKS everyone else, but trammell is my HERO today.
      Thanks again!
        Well if anyone was interested, this is what I did to correct my issue:
        Orignal Code Snipet
        opendir(CacheDir,$Cache) || die "Couldn't open the directory $Cache: $ +!\n"; @filelist=grep { /[0-9]{5}/ } readdir(CacheDir); closedir(CacheDir); @sortlist=sort {$b <=> $a} @filelist; for ($i=0;$i<$MsgCount;$i++) { $filename=$sortlist[$i]; if ($filename) { open(ITEM,"$Cache/$filename");

        New Code
        opendir(CacheDir,$Cache) || die "Couldn't open the directory $Cache: $ +!\n"; @filelist=grep { /[0-9]{5}/ } readdir(CacheDir); closedir(CacheDir); $z=0; while($filelist[$z]){ if ($filelist[$z] =~ /[0][0][0-1][0-9][0-9]/){ $bigfilelist[$z]=int($filelist[$z]+100000); } else{ $bigfilelist[$z]=$filelist[$z]; } ++$z; } @sortlist=sort {$b <=> $a} @bigfilelist; $i=0; while($sortlist[$i]){ $filename=int($sortlist[$i]); if($filename > 100000){ $filename=int($filename-100000); } else{ $filename=int($filename); } $filename=sprintf("%05d", $filename); ++$i; if ($filename) { open(ITEM,"$Cache/$filename");

        Again I wanted to thank everyone for their input, I still learned a few things that I will use in the future.
        Danny
Re: Sorting through a rollover
by ikegami (Pope) on Feb 10, 2005 at 18:56 UTC
    # #################### # # BUG: Assumes less than 5000 messages per minute. # There is not enough data to avoid this problem. # # BUG: Does not handle messages across different years correctly. # There is not enough data to avoid this problem. # # #################### use strict; use warnings; use vars qw( %MONTH_TO_INDEX ); BEGIN { %MONTH_TO_INDEX = qw( Jan 1 Feb 2 Mar 3 Apr 4 May 5 Jun 6 Jul 7 Aug 8 Sep 9 Oct 10 Nov 11 Dec 12 ); } my $header = <DATA>; my @data = <DATA>; my @sort_data = map { $_->[5] } sort { $a->[0] <=> $b->[0] || $a->[1] <=> $b->[1] || $a->[2] <=> $b->[2] || $a->[3] <=> $b->[3] || (abs($a->[4] - $b->[4]) < 5000 ? ($a->[4] <=> $b->[4]) : ($b->[4] <=> $a->[4]) ) } map { /^(\d+) \| (\D+)(\d+) \| (\d+):(\d+)/; [ $MONTH_TO_INDEX{$2}, # 0: Month 0+$3, # 1: Day 0+$4, # 2: Hour 0+$5, # 3: Minute 0+$1, # 4: id $_, # 5: Input ] } @data; print($header, @sort_data); __DATA__ Transid | Date | Time | PagerID | Message Description 00004 | Feb10 | 11:18 | sundallas_e | dspmmr3 at ... 00005 | Feb10 | 11:18 | sundallas_e | dspmmr3 at ... 99996 | Feb10 | 11:18 | sundallas_e | dspmmr3 at ... 00000 | Feb11 | 11:17 | hpdallas_e | zcompass at ... 00001 | Feb10 | 11:17 | hpdallas_e | zcompass at ... 99995 | Feb10 | 11:17 | hpdallas_e | zcompass at ...

    Outputs:

    Transid | Date | Time | PagerID | Message Description 99995 | Feb10 | 11:17 | hpdallas_e | zcompass at ... 00001 | Feb10 | 11:17 | hpdallas_e | zcompass at ... 99996 | Feb10 | 11:18 | sundallas_e | dspmmr3 at ... 00004 | Feb10 | 11:18 | sundallas_e | dspmmr3 at ... 00005 | Feb10 | 11:18 | sundallas_e | dspmmr3 at ... 00000 | Feb11 | 11:17 | hpdallas_e | zcompass at ...
Re: Sorting through a rollover
by phaylon (Curate) on Feb 10, 2005 at 17:10 UTC
    Hi, I'm sorry but I am not a native english speaker, so please forgive the poor question: Huh?!

    What're you trying to do? Do you want to read the file and make a sorted output with the highest nr on top? Or otherways? As I assume your problem is that 00001 is smaller than 99999, but should be above it, because it's next?
      Yes, once I reach my max 99999 and the next event's transid is 00001, I need it to sort numerically greater than 99999 as to display:
      00001
      99999
      99998
      Thanks.
        Here's my try:

        Update: As I see, Razorblade was first :D
        use warnings; use strict; + my @middle = qw( 49998 49999 50000 50001 50002 50003 ); my @limit = qw( 99997 99998 99999 00001 00002 00003 ); + use Data::Dumper; print "MIDDLE\n"; print Dumper sort ro @middle; print "LIMIT\n"; print Dumper sort ro @limit; + sub ro { ( abs($a - $b) > 5000 ? $b <=> $a : $a <=> $b ) }
        gives me:
        MIDDLE $VAR1 = 49998; $VAR2 = 49999; $VAR3 = 50000; $VAR4 = 50001; $VAR5 = 50002; $VAR6 = 50003; LIMIT $VAR1 = 99997; $VAR2 = 99998; $VAR3 = 99999; $VAR4 = '00001'; $VAR5 = '00002'; $VAR6 = '00003';
        hope that helps;phay
        Is there a reason why you only use 5 digits? The problem I see is that 99999 _is larger_ than 000001. It think it would only work with some kind of "cheating". I wouldn't find it very elegant, because your script has to guess.

        If it's in the lower range, 00001 is more likely the one on the bottom. If the numbers are near the top end, you can assume 00001 as first one.

        Possibilities I see:
        - Using one of the Neuronal Networking Modules on CPAN and train it to decide for you.
        - The index numbers do not have holes (eg: nothin' like 99800, 99801, 99803, ..)
        _ You make a algorithm which rates 0000n higher if the value processed before was larger and higher as, say 99990.
        - Don't let it begin again at 00001

        These are (imho) cool, easy, awful and clear :D

Re: Sorting through a rollover
by RazorbladeBidet (Friar) on Feb 10, 2005 at 17:07 UTC
    Greetings, fellow Texan

    How about adding the number of messages shown (in this case 200) to the trans id and then modding (%) by 100000?
    Not sure why I thought that would work...lemme think on it


    Update

    You could also try sorting by date but that's a whole 'nother mess.