1: Here's a function I wrote to group consecutive sets of integers in to a tabular form.
   2: 
   3: ####
   4: ##
   5: ## my $table_hrf = consecutive( \@integers )
   6: ##
   7: ## Description: function to find consecutive integers
   8: ##
   9: ## Parameters: a reference to an array of integers
  10: ##
  11: ## Returns: table of sorted, grouped integers in the form of
  12: ##
  13: ##            base_integer => sequence_length
  14: ##
  15: ## For instance, the list
  16: ##
  17: ##  qw( 20 2 3 4 5 6 7 23 19 17 25 30 11 12 22 21 68 103829 24 18 );
  18: ##
  19: ## will sort to
  20: ##
  21: ##       2 => 5,
  22: ##      11 => 1,
  23: ##      17 => 8,
  24: ##      30 => 0,
  25: ##      68 => 0,
  26: ##  103829 => 0
  27: ##
  28: ## which means
  29: ##
  30: ##  sequence(5): 2 to 7
  31: ##  sequence(1): 11 to 12
  32: ##  sequence(8): 17 to 25
  33: ##  single: 30
  34: ##  single: 68
  35: ##  single: 103829
  36: ##
  37: ####
  38: sub consecutive
  39: {
  40:   my $integer_arf = shift;
  41: 
  42:   my %table = ( );
  43: 
  44:   my $base = 0;
  45:   my $previous = 0;
  46: 
  47:   foreach my $number ( sort numerically @{ $integer_arf } )
  48:   {
  49:     if( ( $number - 1 ) == $previous ) # if the current number is one greater
  50:     {                                  # than the previous, increment our base
  51:       $table{ $base }++;
  52:     }
  53:     else # we've found a new sequence
  54:     {
  55:       $table{ $number } = 0; # we're the base number, so set our adder to 0
  56:       $base = $number;
  57:     }
  58: 
  59:     $previous = $number; # end of loop -- our $number is now old
  60:   }
  61: 
  62:   return \%table;
  63: }
  64: 
  65: sub numerically { $a <=> $b }

Replies are listed 'Best First'.
Re: grouping consecutive integers
by hv (Prior) on Apr 15, 2003 at 16:13 UTC

    Because you initialise $previous to 0, the results will be incorrect if the lowest number in the list is 1. I'd avoid that problem by using undef to signal that there is no previous value:

    my $previous; ... if (defined($previous) && $previous == $number - 1) { $table{$base}++; } else { ... }

    Hugo
Re: grouping consecutive integers
by artist (Parson) on Apr 15, 2003 at 16:29 UTC
    Here is something closer that you can use:
    use Set::IntSpan; use strict; sub consecutive_integers_list { my(@integers) =sort { $a <=> $b } @{+shift}; my $set = Set::IntSpan->new(join ",", @integers); $set->run_list; } my @integers = qw( 20 2 3 4 5 6 7 23 19 17 25 30 11 12 22 21 68 103829 + 24 18 ); print consecutive_integers_list(\@integers); #prints: 2-7,11-12,17-25,30,68,103829
    artist
Re: grouping consecutive integers
by Juerd (Abbot) on Apr 17, 2003 at 09:01 UTC

    Here's a function I wrote to group consecutive sets of integers in to a tabular form.

    sub consecutive { local $_ = join " ", sort { $a <=> $b } grep !/\D/, @_; s[ (\d+ \s*) ( (??{$++1}) \s* )* ] "$1 => ${\($+-$1)},"gx; return eval "{ $_ }"; } use Data::Dumper; print Dumper(consecutive qw(20 2 3 4 5 6 7 23 19 17 25 30 11 12 22 21 +68 103829 24 18));
    Yay for strings and regexes :)

    Juerd
    - http://juerd.nl/
    - spamcollector_perlmonks@juerd.nl (do not use).
    

Re: grouping consecutive integers
by CountZero (Bishop) on Apr 18, 2003 at 18:53 UTC

    It shows some odd behaviour if you have duplicate numbers in your input array.

    qw( 20 2 3 4 5 6 7 23 19 5 17 25 30 11 4 12 22 5 21 68 103829 24 18 )

    results in

    103829->0 2->2 4->1 11->1 5->2 68->0 30->0 17->8
    It seems duplicate numbers start new sequences but triplicate (or quadruplicate, ...) numbers don't start separate sequences of their own.

    The 4->1 really meaning that 4 is followed by another 4!

    CountZero

    "If you have four groups working on a compiler, you'll get a 4-pass compiler." - Conway's Law