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

i need help...im trying to create a way to take a string of numbers seperated by commas, sort them numerically (ala Sorting by_number, credit to ncw), and in the process, turn any consecutive group of numbers into a range (eg: 3,4,5,6,7 would be 3-7), then rejoin all the numbers and ranges of numbers by commas again...now, i've written a script to do this, but it's not even coming close to working, and i can't figure out why...it might be some small thing i'm overlooking, like a missing dash or bracket, or it might be my whole method of doing it...(TMTOWTDI)
# example input string $in = '1089,3,4,5,6,7,99,832,1087,831,1088'; @i = split(/,/,$in); # resort the array numerically @read = sort by_number @i; $list = ''; foreach $i (0..$#read) { if(($read[$i]+1) == $read[$i+1]) { $list .= $read[$i].'-'.$read[$i+1].','; } else { $list .= $read[$i].','; } } $list =~ s/(\d+)\,\1\-/\1\,/g; # output the finished product print $list; # again, credit to ncw for the sort method sub by_number { my @a = split /(\d+)/, $a; my @b = split /(\d+)/, $b; while (@a && @b) { my ($aa, $bb) = (shift(@a), shift(@b)); my $res = ($aa =~ /^\d/ && $bb =~ /^\d/) ? $aa <=> $bb : $aa cmp $bb ; return $res if $res; } return @a <=> @b; }

Replies are listed 'Best First'.
Re: Sorting and ranging problems...
by Trimbach (Curate) on Sep 27, 2001 at 03:30 UTC
    First off, you don't need a fancy sort subroutine if all you're using is regular numbers. The famous "spaceship operator" (<=>) will do quite nicely.(see below.)
    #!/usr/bin/perl -w use strict; # example input string my $in = '1089,3,4,5,6,7,99,832,1087,831,1088'; # split the scalar my @i = split(/,/,$in); # to sort by number just use $a <=> $b instead # of the default $a cmp $b @i = sort {$a <=> $b} @i; # load a hash to make looking up existing numbers easy my %lookup; foreach my $number(@i) { $lookup{$number} = 1; } # now check the numbers for consecutiveness, and # put the results in @output my @output; my $end_number=0; foreach my $number (@i) { # skip the numbers already included in a range next if $number <= $end_number; # find the end_number in a range $end_number = $number; $end_number++ until !$lookup{$end_number+1}; # and add to @output whether the range has # one or more numbers in it if ($number == $end_number) { push @output, $number; } else { push @output, "$number-$end_number"; } } print "$_\n" for @output;
    There's lots more fancy ways of doing this. The code above is optimized for read-ability and learn-ability.

    (and, for the record, yes, it looks a little homework-ey, but anyone who posts code has at least given it a shot.)

    Gary Blackburn
    Trained Killer

(Golf!) Re: Sorting and ranging problems...
by demerphq (Chancellor) on Sep 27, 2001 at 05:45 UTC
    I thought that perhaps dragonchild was right and that this is most likely homework, so I decided to make my solution a learning excercise for myself as well and provide something to make you think. So I did it using regexes and turned it into golf....

    I think I Tee-off at 198 chars... Which if you exclude the length of the input string is 163 (counting quotes still though)

    $"=",";@0=sort{$a<=>$b}split/,/,'1089,3,4,5,6,7,99,832,1087,831,1088'; +$_= ":@0";1while s;:((\d+)(-(\d+))?),(\d+);$4?$5-$4==1?":$2-$5":",$1:$5":( +$5- $2==1)?":$2-$5":",$2:$5";e;y/:/,/d;s/^.//;print;
    Oh yeah, its strict safe as well... But somehow, if this is homework, I doubt your prof's gunna like this one....

    :-)

    Note
    I took the liberty of assuming the numbers would be unique. Oh and thanks to Chemboy for the golf language tips :-)

    Yves
    --
    You are not ready to use symrefs unless you already know why they are bad. -- tadmc (CLPM)

Re: Sorting and ranging problems...
by dragonchild (Archbishop) on Sep 27, 2001 at 03:07 UTC
    Interesting homework problem. However, I'm in a generous mood. Next time, actually think before you post.
    my @list; my ($first, $last) = (undef, undef); NUM: foreach my $num (@read) { unless (defined $first) { $first = $last = $num; next NUM; } if ($num > ($last + 1)) { push @list, ($last > $first ? "$first-$last" : $first); $first = $last = $num; next NUM; } $last = $num; } push @list, ($last > $first ? "$first-$last" : $first) if defined $first;

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

      what do you mean 'homework problem'? i need this for a message board program...a unique one in the aspect that it keeps track of which user has viewed which post, and doesnt display the ones they've read...(it stores read posts in a cookie)