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

Brother Monks,

A devilishly simple perl programming problem has hit me while trying to output some information to a web page in a condensed form. <as opposed to that amazingly long and uncondensed sentence>

I'm trying to condense a list of numbers e.g. (1 2 3 4 8 9) to a list of ranges (1-4,8,9). As I am reading and creating the data it can be stored in any form but I have chosen an array at this time so the program can walk through the list and it can be sorted if necessary. The actual lists of numbers may be quite a bit longer than the examples so this really would help in display space.

I did find the module Perlbug::Object::Range, but it comes with a LOT of baggage. It also doesn't handle singles nicely. It outputs (1-4,8-9) which is OK, but not what I would like to really see. My install of this module also seems to double up on single last numbers (1 2 3 4 8) becomes (1-4,8-8). I don't know if that is the intended action, but it is definitely not right.

Would any of the monks out there have a module or pre-written code to do this? I have started writing something, but I am still a perl novice and this works out to be a rather complex set of logic. I would rather use a pre-made wheel than carve my own so I can stick to the actual logic of the program and not worry so much about writing the formatting routines. I know it is a little lazy, but don't we all use standard CPAN modules so we can be just a little lazy.

-Kurt

Replies are listed 'Best First'.
Re: condense list of numbers to ranges
by Kanji (Parson) on Jan 28, 2003 at 22:57 UTC
      Many thanks,

      Obviously didn't use the right search terms in super-search.

      -Kurt

Re: condense list of numbers to ranges
by Gilimanjaro (Hermit) on Jan 28, 2003 at 23:29 UTC
    I'm actually proud of this one:

    @numbers = (1,2,3,4,8,9,10,13,14,18,19,20,24); for(@numbers) { push @$range,$_ and next if $range and $_-$range->[-1]==1; push @ranges, ($range = [$_]); } $rangelist = join(',', map { @$_>2 ? "$_->[0]-$_->[-1]" : @$_ } @ranges ); print "($rangelist)";

    The for-loop builds an array of arrays, where each subarray is a sequence on consecutive numbers. The join/map turns the bigger subarrays into the '-' notation.

    Happy coding!

    Update: Yes, I agree, the regex is cooler... I still like mine though!

Re: condense list of numbers to ranges
by cees (Curate) on Jan 29, 2003 at 04:15 UTC

    This was question #6 of the Perl Quiz of the Week. If you want some different ways to solve this one, look at the solutions that the list members came up with, or look at MJD's summary of the quiz.

    The one from Andreas Koenig was probably the shortest and easiest of the solutions:

    use Set::IntSpan; # CPAN rules :-) use strict; sub format_number_list { my(@n) = @_; my $set = Set::IntSpan->new(join ",", @n); my $run = $set->run_list; $run =~ s/,/, /g; # give them the spaces $run; }
Re: condense list of numbers to ranges
by shotgunefx (Parson) on Jan 29, 2003 at 09:08 UTC
    My own take Yet Another List to Range function.

    I wrote this because most of the other solutions on the site I tried would fail or didn't do what quite I wanted.

    -Lee

    "To be civilized is to deny one's nature."