Front page | perl.beginners |
Postings from December 2001
Re: summarizing a list of numbers?
Thread Previous
From:
Chris Ball
Date:
December 17, 2001 07:05
Subject:
Re: summarizing a list of numbers?
Message ID:
20011217151143.A19947@void.printf.net
On Mon, Dec 17, 2001 at 01:19:43PM +0100, Kredler Stefan wrote:
> I want to transform a sorted list into a compact list (if this is the
> correct term).
> e.g. my list is
>
> 9,11,12,13,14,23,25,26,27,50 and want to have something like
> 9,11-14,23,25-27,50 (to pass on to a unix-command).
>
> Is there any module or function I can use to summarize or compact this?
Don't know. I decided that this was the sort of thing I shouldn't have
any problem in writing, though, and ended up spending most of my lunch
break on it. It seems to work for the sort of data you gave:
void:chris~ % perl compact.pl 9 11 12 13 14 23 25 26 27 50
9,11-14,23,25-27,50
I'd be really interested to see if anyone can come up with a better way
of doing this; one that doesn't have to keep state, or uses recursion.
I'll bow down to anyone who can make a one-liner of it, naturally.
Hope this helps - out of interest, what sort of UNIX command _needs_ a
list like that instead of an explicit one?
- ~C.
void:chris~ % cat compact.pl
#!/usr/bin/perl -w
# Author: Chris Ball <chris@cpan.org>
# Function: Provide a 'compact list' for an array of numbers.
# Also at: http://printf.net/compact.pl
my @approx = @ARGV;
my ($i, $inrange, $start, $end, $more);
use strict;
# Overview:
# - Loop through the array.
# - If the element after current matches (cur+1):
# - Extend a range if we're in one.
# - Set up a range if we aren't.
# - If the element after current _isn't_ (cur+1):
# - Mark/print the end of a range, or
# - Print a number on its own if we weren't in a range.
# - If the element after current just doesn't exist at all:
# - Finish off a range or number tidily.
map { # Loop through the array, using $_.
my $cur = $approx[$_];
my $next = $approx[$_+1] if defined $approx[$_+1];
if ($next) { # Is there an array element after this one?
if ($next != ($cur + 1)) {
# If the next element _isn't_ current+1..
if ($inrange) {
# If we're in a range, it needs to be finished.
print "$start-$end,";
$inrange = 0;
}
else {
# We're not in a range.
print "$cur,";
}
}
else {
# If the next array element is cur+1.
if ($inrange) {
# If we're already in a range, extend it by 1.
$end++;
}
if (not $inrange) {
# We're not in a range. Set start/end/inrange.
$start = $approx[$_];
$end = $approx[$_+1];
$inrange = 1;
}
}
}
else {
# It's the last element. Make things look tidy.
if ($inrange) {
print "$start-$end\n";
}
else {
print "$cur\n";
}
}
} 0..$#approx
# -- end.
--
Chris Ball E-mail: chris.ball@fast.no
Web Engineer Web : www.fast.no
Fast Web Media Ltd Try : www.alltheweb.com
12th Floor Sunlight House, Quay Street, Manchester M3 3JZ, UK.
Thread Previous