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 <[EMAIL PROTECTED]>
# 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: [EMAIL PROTECTED]
Web Engineer               Web   : www.fast.no
Fast Web Media Ltd         Try   : www.alltheweb.com 
12th Floor Sunlight House, Quay Street, Manchester M3 3JZ, UK.

-- 
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to