Range extraction: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|OCaml}}: simplified range_extract)
No edit summary
Line 550: Line 550:
Output:
Output:
0-2,4,6-8,11,12,14-25,27-33,35-39
0-2,4,6-8,11,12,14-25,27-33,35-39

=={{header|C sharp}}==
<lang csharp>using System;
using System.Collections.Generic;
using System.Linq;

class RangeExtraction
{
static void Main()
{
const string testString = "0, 1, 2, 4, 6, 7, 8, 11, 12, 14,15, 16, 17, 18, 19, 20, 21, 22, 23, 24,25, 27, 28, 29, 30, 31, 32, 33, 35, 36,37, 38, 39";
var result = String.Join(",", RangesToStrings(GetRanges(testString)));
Console.Out.WriteLine(result);
}

public static IEnumerable<IEnumerable<int>> GetRanges(string testString)
{
var numbers = testString.Split(new[] { ',' }).Select(x => Convert.ToInt32(x));
var current = new List<int>();
foreach (var n in numbers)
{
if (current.Count == 0)
{
current.Add(n);
}
else
{
if (current.Max() + 1 == n)
{
current.Add(n);
}
else
{
yield return current;
current = new List<int> { n };
}
}
}
yield return current;
}

public static IEnumerable<string> RangesToStrings(IEnumerable<IEnumerable<int>> ranges)
{
foreach (var range in ranges)
{
if (range.Count() == 1)
{
yield return range.Single().ToString();
}
else
{
yield return range.Min() + "-" + range.Max();
}
}
}
}
</lang>


=={{header|Common Lisp}}==
=={{header|Common Lisp}}==

Revision as of 03:57, 31 October 2011

Task
Range extraction
You are encouraged to solve this task according to the task description, using any language you may know.

A format for expressing an ordered list of integers is to use a comma separated list of either

  • individual integers
  • Or a range of integers denoted by the starting integer separated from the end integer in the range by a dash, '-'. (The range includes all integers in the interval including both endpoints)
  • The range syntax is to be used only for, and for every range that expands to more than two values.

Example
The list of integers:

-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20

Is accurately expressed by the range expression:

-6,-3-1,3-5,7-11,14,15,17-20

(And vice-versa).

The task

  • Create a function that takes a list of integers in increasing order and returns a correctly formatted string in the range format.
  • Use the function to compute and print the range formatted version of the following ordered list of integers:
    0,  1,  2,  4,  6,  7,  8, 11, 12, 14,
   15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
   25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
   37, 38, 39
  • Show the output of your program.

C.f. Range expansion

Ada

The provided solutions return an empty string, if the Sequence of integers is empty. Ranges with negative bounds are represented as -9--4, as the task requires. For real-life applications it is better to use the notation -9..-4.

Iterative Solution

Since we don't know in advance how long the output will be, the iterative solution uses Unbounded_Strings.

<lang Ada>with Ada.Text_IO; use Ada.Text_IO; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with Ada.Strings.Fixed; use Ada.Strings.Fixed;

procedure Range_Extraction is

  type Sequence is array (Positive range <>) of Integer;
  function Image (S : Sequence) return String is
     Result : Unbounded_String;
     From   : Integer;
     procedure Flush (To : Integer) is
     begin
        if Length (Result) > 0 then
           Append (Result, ',');
        end if;
        Append (Result, Trim (Integer'Image (From), Ada.Strings.Left));
        if From < To then
           if From+1 = To then
              Append (Result, ',');
           else
              Append (Result, '-');
           end if;
           Append (Result, Trim (Integer'Image (To), Ada.Strings.Left));
        end if;
     end Flush;
  begin
     if S'Length > 0 then
        From := S (S'First);
        for I in S'First + 1..S'Last loop
           if S (I - 1) + 1 /= S (I) then
              Flush (S (I - 1));
              From := S (I);
           end if;
        end loop;
        Flush (S (S'Last));
     end if;
     return To_String (Result);
  end Image;

begin

  Put_Line
    (  Image
         (  (  0,  1,  2,  4,  6,  7,  8, 11, 12, 14,
               15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
               25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
               37, 38, 39
            )  )  );

end Range_Extraction;</lang>


Recursive Solution

The recursive solution avoids the usage of unbounded strings.

<lang Ada>with Ada.Text_IO, Ada.Strings.Fixed;

procedure Range_Extract is

  type Sequence is array (Positive range <>) of Integer;
  function Img(I: Integer) return String is -- the image of an Integer
  begin
     return
       Ada.Strings.Fixed.Trim(Integer'Image(I), Ada.Strings.Left);
  end Img;
  function Img(S: Sequence) return String is -- the image of a Sequence
     function X(S : Sequence) return String is -- recursive eXtract
        Idx: Positive := S'First;
     begin
        if S'Length = 0 then return
          ""; -- return nothing if Sequence is empty
        else
           while Idx < S'Last and then S(Idx+1) = S(Idx) + 1 loop
              Idx := Idx + 1;
           end loop;
           if Idx = S'First then return
             "," & Img(S(Idx)) & X(S(Idx+1 .. S'Last));
           elsif Idx = S'First+1 then return
             "," & Img(S(S'First)) & ',' & Img(S(Idx)) & X(S(Idx+1 .. S'Last));
           else return
             "," & Img(S(S'First)) & '-' & Img(S(Idx)) & X(S(Idx+1 .. S'Last));
           end if;
        end if;
     end X;
  begin -- function Img(S: Sequence) return String
     if S'Length = 0 then return
       "";
     else return
       Img(S(S'First)) & X(S(S'First+1 .. S'Last));
     end if;
  end Img;

begin -- main

  Ada.Text_IO.Put_Line(Img( ( 0,  1,  2,  4,  6,  7,  8, 11, 12, 14, 15, 16,
                              17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 29,
                              30, 31, 32, 33, 35, 36, 37, 38, 39) ));

end Range_Extract;</lang>

Sample output

The sample output is exactly the same, for both solutions:

0-2,4,6-8,11,12,14-25,27-33,35-39

ALGOL 68

This example is incorrect. Please fix the code and remove this message.

Details: Range format is with a dash and no spaces, plus ranges must cover >2 integers, thanks.

Note: The following Iterative code specimen is the "unrolled" version of the Generative code specimen below. Together they provided as a comparison of the two different methods.

Iterative

Works with: ALGOL 68 version Revision 1 - no extensions to language used
Works with: ALGOL 68G version Any - tested with release 1.18.0-9h.tiny
  • The closest concept that Algol 68 has to duck typing is the tagged union. This is used to define mode rangeint = union(int, struct(int lwb, upb)). If duck typing was available it could reduced the size of the code specimen, but would have lost some of Algol 68's strong type data security.

<lang algol68>MODE INTLIST = FLEX[0]INT;

  1. Declarations for manipulating lists of range pairs [lwb:upb] #

MODE RANGE = STRUCT(INT lwb, upb); MODE RANGELIST = FLEX[0]RANGE;

PROC range repr = (RANGE range)STRING:

 whole(lwb OF range,0)+
   IF lwb OF range = upb OF range THEN "" ELSE ":"+whole(upb OF range,0) FI;
  1. OP REPR = (RANGE range)STRING: range repr(range); firmly related to RANGEINT #
  1. Declarations for manipulating lists containing pairs AND lone INTs #

MODE RANGEINT = UNION(INT, RANGE); MODE RANGEINTLIST = FLEX[0]RANGEINT;

PROC range int repr = (RANGEINT range int)STRING:

 CASE range int IN
   (RANGE range): range repr(range),
   (INT int): whole(int,0)
 ESAC;

OP REPR = (RANGEINT range int)STRING: range int repr(range int);

  1. The closest thing ALGOL 68 has to inheritance is the union #

MODE RANGEINTLISTINIT = UNION(RANGEINTLIST, RANGELIST, INTLIST);

PROC range int list repr = (RANGEINTLIST range int list)STRING: (

 STRING out := "(", sep := "";
 FOR key FROM LWB range int list TO UPB range int list DO
   out +:= sep + REPR range int list[key];
   sep := ", "
 OD;
 out+")"

);

OP REPR = (RANGEINTLIST range int list)STRING: range int list repr(range int list);

  1. Task portion #

PROC range int list init = (RANGEINTLISTINIT range int list)RANGEINTLIST: (

 [LWB range int list: UPB range int list]RANGEINT out range int list;
 INT upb out range int list := LWB out range int list - 1;
 UNION(VOID, RANGE) prev range := EMPTY;
 PROC out range int list append = (RANGE value)VOID:
   out range int list[upb out range int list+:=1] :=
     IF lwb OF value = upb OF value THEN lwb OF value ELSE value FI;
  1. Note: Algol 68RS cannot handle LWB and UPB of a UNION in the following: #
 FOR key FROM LWB range int list TO UPB range int list DO
   RANGEINT value = CASE range int list IN 
                      (INTLIST list):list[key],
                      (RANGELIST list):list[key],
                      (RANGEINTLIST list):list[key]
                    ESAC;
   RANGE next range := CASE value IN
       (RANGE range): range,
       (INT value): RANGE(value, value)
     ESAC;
   prev range := 
     CASE prev range IN
       (VOID): next range,
       (RANGE prev range): 
         IF upb OF prev range + 1 = lwb OF next range THEN
           RANGE(lwb OF prev range, upb OF next range) # merge the range #
         ELSE
           IF lwb OF prev range <= upb OF prev range THEN
             out range int list append(prev range)
           FI;
           next range
         FI
       OUT SKIP
     ESAC
 OD;
 CASE prev range IN 
   (RANGE last range): out range int list append(last range)
 ESAC;
 out range int list[:upb out range int list]

);

  1. do some simple test cases: #

test: BEGIN

 []INT int list = (
   0,  1,  2,  4,  6,  7,  8, 11, 12, 14,
   15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
   25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
   37, 38, 39);
 
 []RANGE range list = ( # unnormalised #
   (0,0),  (1,1),  (2,2),  (4,4),  (6,6),  (7,7),  (8,8), (11,11), (12,12), (14,14),
   (15,15), (16,16), (17,17), (18,18), (19,19), (20,20), (21,21), (22,22), (23,23), (24,24),
   (25,25), (27,27), (28,28), (29,29), (30,30), (31,31), (32,32), (33,33), (35,35), (36,36),
   (37,37), (38,38), (39,39));
 
 []RANGEINT list a = (RANGE(0,2), 4, RANGE(6,8), RANGE(11,12), RANGE(14,25), RANGE(27,33), RANGE(35,39));
 []RANGEINT list b = ( # unnormalised #
   0,  1,  2,  4,  6,  7,  8, 11, 12, 14,
   15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
   25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
   37, 38, 39);
 FLEX[0]RANGEINT list c := range int list init(list b); # normalised #
 
  1. compare manipulation of various types of argument lists #
 print((REPR range int list init(int list), new line));
 print((REPR range int list init(range list), new line));
 print((REPR list a, new line));
 print((REPR(range int list init(list b)), new line));
 print((REPR list c, new line))

END</lang> Output:

(0:2, 4, 6:8, 11:12, 14:25, 27:33, 35:39)
(0:2, 4, 6:8, 11:12, 14:25, 27:33, 35:39)
(0:2, 4, 6:8, 11:12, 14:25, 27:33, 35:39)
(0:2, 4, 6:8, 11:12, 14:25, 27:33, 35:39)
(0:2, 4, 6:8, 11:12, 14:25, 27:33, 35:39)

Generative

Works with: ALGOL 68 version Revision 1 - no extensions to language used
Works with: ALGOL 68G version Any - tested with release 1.18.0-9h.tiny
  • The following code a set of helper functions/generators that can be used to manipulate a lists of ranges. They can manipulate either arrays or iterator. And they can handle data of type int or range and both these types unioned.

These chained iterators do the following steps:

  1. Iterate through three different types of initial arrays - []int, []range and []rangeint with gen range, yielding range(lwb,upb)
  2. Iterate with gen range merge yielding merged range(lwb,upb)
  3. Iterate with gen range int merge, merging and yielding a union of int and range
  4. Finally iterate with range int list init exiting with an array of union of int and range.

<lang algol68>MODE INTLIST = FLEX[0]INT; MODE YIELDINT = PROC(INT)VOID;

  1. Declarations for manipulating lists of range pairs [lwb:upb] #

MODE RANGE = STRUCT(INT lwb, upb); MODE RANGELIST = FLEX[0]RANGE; MODE YIELDRANGE = PROC(RANGE)VOID;

PROC range repr = (RANGE range)STRING:

 whole(lwb OF range,0)+
   IF lwb OF range = upb OF range THEN "" ELSE ":"+whole(upb OF range,0) FI;
  1. OP REPR = (RANGE range)STRING: range repr(range); firmly related to RANGEINT #
  1. Declarations for manipulating lists containing pairs AND lone INTs #

MODE RANGEINT = UNION(INT, RANGE); MODE RANGEINTLIST = FLEX[0]RANGEINT; MODE YIELDRANGEINT = PROC(RANGEINT)VOID;

PROC range int repr = (RANGEINT range int)STRING:

 CASE range int IN
   (RANGE range): range repr(range),
   (INT int): whole(int,0)
 ESAC;

OP REPR = (RANGEINT range int)STRING: range int repr(range int);

  1. The closest thing ALGOL 68 has to inheritance is the union #

MODE RANGEINTLISTINIT = UNION(RANGEINTLIST, RANGELIST, INTLIST);

PROC range int list repr = (RANGEINTLIST range int list)STRING: (

 STRING out := "(", sep := "";
 FOR key FROM LWB range int list TO UPB range int list DO
   out +:= sep + REPR range int list[key];
   sep := ", "
 OD;
 out+")"

);

OP REPR = (RANGEINTLIST range int list)STRING: range int list repr(range int list);

  1. Note: Algol 68RS cannot handle LWB and UPB of a UNION in the following: #

PROC gen range = (RANGEINTLISTINIT range int list, YIELDRANGE yield range)VOID:

 FOR key FROM LWB range int list TO UPB range int list DO
   RANGEINT value = CASE range int list IN 
                      (INTLIST list):list[key],
                      (RANGELIST list):list[key],
                      (RANGEINTLIST list):list[key]
                    ESAC;
   yield range(
     CASE value IN
       (RANGE range): range,
       (INT value): (value, value)
     ESAC
   )
 OD;

PROC gen range merge = (RANGEINTLISTINIT range int list, YIELDRANGE yield range)VOID: (

 UNION(VOID, RANGE) prev range := EMPTY;
  1. FOR RANGE next range IN # gen range(range int list, # ) DO #
    1. (RANGE next range)VOID:
  2. if the ranges cannot be merge, then yield 1st, and return 2nd #
   prev range := 
     CASE prev range IN
       (VOID): next range,
       (RANGE prev range): 
         IF upb OF prev range + 1 = lwb OF next range THEN
           RANGE(lwb OF prev range, upb OF next range) # merge the range #
         ELSE
           IF lwb OF prev range <= upb OF prev range THEN
             yield range(prev range)
           FI;
           next range
         FI
       OUT SKIP
     ESAC
  1. OD # );
 CASE prev range IN (RANGE last range): yield range(last range) ESAC

);

PROC gen range int merge = (RANGEINTLISTINIT range int list, YIELDRANGEINT yield range int)VOID: (

  1. FOR RANGE range IN # gen range merge(range int list, # ) DO #
    1. (RANGE range)VOID:
   yield range int(
     IF lwb OF range = upb OF range THEN lwb OF range ELSE range FI
   )
  1. OD # )

);

PROC range int list init = (RANGEINTLISTINIT range int list)RANGEINTLIST: (

 [LWB range int list: UPB range int list]RANGEINT out range int list;
 INT upb out range int list := LWB out range int list - 1;
  1. FOR RANGEINT range int IN # gen range int merge(range int list, # ) DO #
    1. (RANGEINT range int)VOID:
   out range int list[upb out range int list+:=1] := range int
  1. OD # );
 out range int list[:upb out range int list]

);

  1. do some simple test cases: #

test: BEGIN

 []INT int list = (
   0,  1,  2,  4,  6,  7,  8, 11, 12, 14,
   15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
   25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
   37, 38, 39);
 
 []RANGE range list = ( # unnormalised #
   (0,0),  (1,1),  (2,2),  (4,4),  (6,6),  (7,7),  (8,8), (11,11), (12,12), (14,14),
   (15,15), (16,16), (17,17), (18,18), (19,19), (20,20), (21,21), (22,22), (23,23), (24,24),
   (25,25), (27,27), (28,28), (29,29), (30,30), (31,31), (32,32), (33,33), (35,35), (36,36),
   (37,37), (38,38), (39,39));
 
 []RANGEINT list a = (RANGE(0,2), 4, RANGE(6,8), RANGE(11,12), RANGE(14,25), RANGE(27,33), RANGE(35,39));
 []RANGEINT list b = ( # unnormalised #
   0,  1,  2,  4,  6,  7,  8, 11, 12, 14,
   15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
   25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
   37, 38, 39);
 FLEX[0]RANGEINT list c := range int list init(list b); # normalised #
 
  1. compare manipulation of various types of argument lists #
 print((REPR range int list init(int list), new line));
 print((REPR range int list init(range list), new line));
 print((REPR list a, new line));
 print((REPR(range int list init(list b)), new line));
 print((REPR list c, new line))

END</lang> Output:

(0:2, 4, 6:8, 11:12, 14:25, 27:33, 35:39)
(0:2, 4, 6:8, 11:12, 14:25, 27:33, 35:39)
(0:2, 4, 6:8, 11:12, 14:25, 27:33, 35:39)
(0:2, 4, 6:8, 11:12, 14:25, 27:33, 35:39)
(0:2, 4, 6:8, 11:12, 14:25, 27:33, 35:39)

AutoHotkey

<lang AutoHotkey>msgbox % extract("0,1,2,4,6,7,8,11,12,14,15,16,17,18,19,20,21,22,23,24,25,27,28,29,30,31,32,33,35,36,37,38,39")

extract( list ) {

   loop, parse, list, `,, %A_Tab%%A_Space%`r`n
   {
       if (A_LoopField+0 != p+1)
           ret .= (f!=p ? (p>f+1 ? "-" : ",") p : "") "," f := A_LoopField
       p := A_LoopField
   }
   return SubStr(ret (f!=p ? (p>f+1 ? "-" : ",") p : ""), 2)

}</lang>

Output:

---------------------------
Range extraction.ahk
---------------------------
0-2,4,6-8,11,12,14-25,27-33,35-39
---------------------------
OK   
---------------------------

C

Using the fine tradition of snprintf, rprint is not responsible for allocating output buffer. It prints the range only if supplied a non-null pointer, but always returns the output length sans the terminating null, so caller can allocate buffer. <lang c>#include <stdio.h>

  1. include <stdlib.h>

size_t rprint(char *s, int *x, int len) {

  1. define sep (a > s ? "," : "") /* use comma except before first output */
  2. define ol (s ? 100 : 0) /* print only if not testing for length */

int i, j; char *a = s; for (i = j = 0; i < len; i = ++j) { for (; j < len - 1 && x[j + 1] == x[j] + 1; j++);

if (i + 1 < j) a += snprintf(s?a:s, ol, "%s%d-%d", sep, x[i], x[j]); else while (i <= j) a += snprintf(s?a:s, ol, "%s%d", sep, x[i++]); } return a - s;

  1. undef sep
  2. undef ol

}

int main() { int x[] = { 0, 1, 2, 4, 6, 7, 8, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39 };

char *s = malloc(rprint(0, x, sizeof(x) / sizeof(int)) + 1); rprint(s, x, sizeof(x) / sizeof(int)); printf("%s\n", s);

return 0; }</lang>output<lang>0-2,4,6-8,11,12,14-25,27-33,35-39</lang>

C++

<lang cpp>

  1. include <iostream>
  2. include <iterator>
  3. include <cstddef>

template<typename InIter>

void extract_ranges(InIter begin, InIter end, std::ostream& os)

{

 if (begin == end)
   return;
 int current = *begin++;
 os << current;
 int count = 1;
 while (begin != end)
 {
   int next = *begin++;
   if (next == current+1)
     ++count;
   else
   {
     if (count > 2)
       os << '-';
     else
       os << ',';
     if (count > 1)
       os << current << ',';
     os << next;
     count = 1;
   }
   current = next;
 }
 if (count > 1)
   os << (count > 2? '-' : ',') << current;

}

template<typename T, std::size_t n>

T* end(T (&array)[n])

{

 return array+n;

}

int main() {

 int data[] = { 0,  1,  2,  4,  6,  7,  8, 11, 12, 14,
                15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
                25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
                37, 38, 39 };
 extract_ranges(data, end(data), std::cout);
 std::cout << std::endl;

} </lang> Output:

0-2,4,6-8,11,12,14-25,27-33,35-39

C#

<lang csharp>using System; using System.Collections.Generic; using System.Linq;

class RangeExtraction {

   static void Main()
   {
       const string testString = "0,  1,  2,  4,  6,  7,  8, 11, 12, 14,15, 16, 17, 18, 19, 20, 21, 22, 23, 24,25, 27, 28, 29, 30, 31, 32, 33, 35, 36,37, 38, 39";
       var result = String.Join(",", RangesToStrings(GetRanges(testString)));
       Console.Out.WriteLine(result);
   }
   public static IEnumerable<IEnumerable<int>> GetRanges(string testString)
   {
       var numbers = testString.Split(new[] { ',' }).Select(x => Convert.ToInt32(x));
       var current = new List<int>();
       foreach (var n in numbers)
       {
           if (current.Count == 0)
           {
               current.Add(n);
           }
           else
           {
               if (current.Max() + 1 == n)
               {
                   current.Add(n);
               }
               else
               {
                   yield return current;
                   current = new List<int> { n };
               }
           }
       }
       yield return current;
   }
   public static IEnumerable<string> RangesToStrings(IEnumerable<IEnumerable<int>> ranges)
   {
       foreach (var range in ranges)
       {
           if (range.Count() == 1)
           {
               yield return range.Single().ToString();
           }
           else
           {
               yield return range.Min() + "-" + range.Max();
           }
       }
   }

} </lang>

Common Lisp

<lang lisp>(defun format-with-ranges (list)

 (unless list (return ""))
 (with-output-to-string (s)
   (let ((current (first list))
         (list    (rest list))
         (count   1))
     (princ current s)
     (dolist (next list)
       (if (= next (1+ current))
           (incf count)
           (progn (princ (if (> count 2) "-" ",") s)
                  (when (> count 1)
                    (princ current s)
                    (princ "," s))
                  (princ next s)
                  (setf count 1)))
       (setf current next))
     (when (> count 1)
       (princ (if (> count 2) "-" ",") s)
       (princ current s)))))

CL-USER> (format-with-ranges (list 0 1 2 4 6 7 8 11 12 14

                                  15 16 17 18 19 20 21 22 23 24
                                  25 27 28 29 30 31 32 33 35 36
                                  37 38 39))

"0-2,4,6-8,11,12,14-25,27-33,35-39" </lang>

D

<lang d>import std.stdio, std.string;

string rangeExtract(int[] items) {

   string[] ranges;
   foreach (i, low; items) {
       while (i < (items.length-1) && (items[i]+1) == items[i+1])
           i++;
       auto hi = items[i];
       if (hi - low >= 2)
           ranges ~= format("%d-%d", low, hi);
       else if (hi - low == 1)
           ranges ~= format("%d,%d", low, hi);
       else
           ranges ~= format("%d", low);
   }
   return ranges.join(",");

}

void main() {

   auto data = [ 0,  1,  2,  4,  6,  7,  8, 11, 12, 14, 15,
                16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27,
                28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39];
   writeln(rangeExtract(data));

}</lang> Output:

0-2,4,6-8,11,12,14-25,27-33,35-39

E

Cheeky solution: relying on the standard library for finding ranges, and just formatting them ourselves.

<lang e>def rex(numbers :List[int]) {

   var region := 0..!0
   for n in numbers { region |= n..n }
   var ranges := []
   for interval in region.getSimpleRegions() { 
       def a := interval.getOptStart()
       def b := interval.getOptBound() - 1
       ranges with= if (b > a + 1) {
                        `$a-$b`
                    } else if (b <=> a + 1) {
                        `$a,$b`
                    } else { # b <=> a
                        `$a`
                    }
   }
   return ",".rjoin(ranges)

}</lang>

<lang e>? rex([ > 0, 1, 2, 4, 6, 7, 8, 11, 12, 14, > 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, > 25, 27, 28, 29, 30, 31, 32, 33, 35, 36, > 37, 38, 39])

  1. value: "0-2,4,6-8,11,12,14-25,27-33,35-39"

</lang>

Euphoria

<lang euphoria>function extract_ranges(sequence s)

   integer first
   sequence out
   out = ""
   if length(s) = 0 then
       return out
   end if
   first = 1
   for i = 2 to length(s) do
       if s[i] != s[i-1]+1 then
           if first = i-1 then
               out &= sprintf("%d,", s[first])
           elsif first = i-2 then
               out &= sprintf("%d,%d,", {s[first],s[i-1]})
           else
               out &= sprintf("%d-%d,", {s[first],s[i-1]})
           end if
           first = i
       end if
   end for
   if first = length(s) then
       out &= sprintf("%d", s[first])
   elsif first = length(s)-1 then
       out &= sprintf("%d,%d", {s[first],s[$]})
   else
       out &= sprintf("%d-%d", {s[first],s[$]})
   end if
   return out

end function

puts(1, extract_ranges({0, 1, 2, 4, 6, 7, 8, 11, 12, 14, 15, 16, 17, 18, 19,

   20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39}))</lang>

Output:

0-2,4,6-8,11,12,14-25,27-33,35-39

F#

<lang fsharp>let extractRanges = function

 | []    -> Seq.empty
 | x::xr ->
     let rec loop ys first last = seq {
       match ys with
       | y::yr when y = last + 1 -> yield! loop yr first y  // add to current range
       | y::yr                   -> yield (first, last)     // finish current range
                                    yield! loop yr y y      //  and start next
       | []                      -> yield (first, last) }   // finish final range
     loop xr x x


let rangeToString (s,e) =

 match e-s with
 | 0 -> sprintf "%d" s
 | 1 -> sprintf "%d,%d" s e
 | _ -> sprintf "%d-%d" s e


let extract = extractRanges >> Seq.map rangeToString >> String.concat ","


printfn "%s" (extract [ 0; 1; 2; 4; 6; 7; 8; 11; 12; 14; 15; 16; 17; 18; 19; 20; 21;

                       22; 23; 24; 25; 27; 28; 29; 30; 31; 32; 33; 35; 36; 37; 38; 39 ])</lang>

Output:

0-2,4,6-8,11,12,14-25,27-33,35-39

Go

<lang go>package main

import (

   "fmt"
   "os"
   "strconv"
   "strings"

)

func main() {

   rf, err := rangeFormat([]int{
       0, 1, 2, 4, 6, 7, 8, 11, 12, 14,
       15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
       25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
       37, 38, 39,
   })
   if err != nil {
       fmt.Println(err)
       return
   }
   fmt.Println("range format:", rf)

}

func rangeFormat(a []int) (string, os.Error) {

   if len(a) == 0 {
       return "", nil
   }
   var parts []string
   for n1 := 0; ; {
       n2 := n1 + 1
       for n2 < len(a) && a[n2] == a[n2-1]+1 {
           n2++
       }
       s := strconv.Itoa(a[n1])
       if n2 == n1+2 {
           s += "," + strconv.Itoa(a[n2-1])
       } else if n2 > n1+2 {
           s += "-" + strconv.Itoa(a[n2-1])
       }
       parts = append(parts, s)
       if n2 == len(a) {
           break
       }
       if a[n2] == a[n2-1] {
           return "", os.NewError(fmt.Sprintf(
               "sequence repeats value %d", a[n2]))
       }
       if a[n2] < a[n2-1] {
           return "", os.NewError(fmt.Sprintf(
               "sequence not ordered: %d < %d", a[n2], a[n2-1]))
       }
       n1 = n2
   }
   return strings.Join(parts, ","), nil

}</lang> Output:

range format: 0-2,4,6-8,11,12,14-25,27-33,35-39

Haskell

<lang haskell>import Data.List (intercalate)

extractRange :: [Int] -> String extractRange = intercalate "," . f

 where f :: [Int] -> [String]
       f (x1 : x2 : x3 : xs) | x1 + 1 == x2 && x2 + 1 == x3
            = (show x1 ++ '-' : show xn) : f xs'
         where (xn, xs') = g (x3 + 1) xs
               g a (n : ns) | a == n    = g (a + 1) ns
                            | otherwise = (a - 1, n : ns)
               g a []                   = (a - 1, [])
       f (x : xs)            = show x : f xs
       f []                  = []</lang>

<lang text>> extractRange $ [0..2] ++ 4 : [6..8] ++ 11 : 12 : [14..25] ++ [27..33] ++ [35..39] "0-2,4,6-8,11,12,14-25,27-33,35-39"</lang>

Icon and Unicon

<lang Icon>procedure main()

  R := [  0,  1,  2,  4,  6,  7,  8, 11, 12, 14, 
         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
         25, 27, 28, 29, 30, 31, 32, 33, 35, 36, 
         37, 38, 39 ]
  write("Input list      := ",list2string(R))
  write("Extracted sting := ",s := range_extract(R)  | "FAILED")

end

procedure range_extract(R) #: return string/range representation of a list of unique integers local s,sep,low,high,x

  every if integer(x:= !R) ~= x then fail                  # ensure all are integers, 
  R := sort(set(R))                                        # unique, and sorted
  s := sep := ""
  while s ||:= sep || ( low := high := get(R) ) do {       # lower bound of range
     sep := ","
     while high := ( R[1] = high + 1 ) do get(R)           # find the end of range
     if high > low+1 then s ||:= "-" || high               # - record range of 3+
     else if high = low+1 then push(R,high)                # - range of 2, high becomes new low
     }
  return s

end

procedure list2string(L) #: helper to convert list to string local s

  every (s := "[ ") ||:= !L || " "
  return s || "]"

end</lang> Sample output:

Input list      := [ 0 1 2 4 6 7 8 11 12 14 15 16 17 18 19 20 21 22 23 24 25 27
28 29 30 31 32 33 35 36 37 38 39 ]
Extracted sting := 0-2,4,6-8,11,12,14-25,27-33,35-39

J

<lang j>require 'strings' fmt=: [: ;@(8!:0) [`]`({. ; (',-' {~ 2 < #) ; {:)@.(2 <. #) group=: <@fmt;.1~ 1 ~: 0 , 2 -~/\ ] extractRange=: ',' joinstring group</lang>

Example use:

<lang j> extractRange 0 1 2 4 6 7 8 11 12 14 15 16 17 18 19 20 21 22 23 24 25 27 28 29 30 31 32 33 35 36 37 38 39 0-2,4,6-8,11,12,14-25,27-33,35-39</lang>

Java

<lang java>public class Range{ public static void main(String[] args){ System.out.println(compress2Range("-6, -3, -2, -1, 0, 1, 3, 4, 5, 7," + " 8, 9, 10, 11, 14, 15, 17, 18, 19, 20"));

       System.out.println(compress2Range(
               "0,  1,  2,  4,  6,  7,  8, 11, 12, 14, " +
               "15, 16, 17, 18, 19, 20, 21, 22, 23, 24," +
               "25, 27, 28, 29, 30, 31, 32, 33, 35, 36," +
               "37, 38, 39"));
   }

   private static String compress2Range(String expanded){
       StringBuilder result = new StringBuilder();
       String[] nums = expanded.replace(" ", "").split(",");
       int firstNum = Integer.parseInt(nums[0]);
       int rangeSize = 0;
       for(int i = 1; i < nums.length; i++){
           int thisNum = Integer.parseInt(nums[i]);
           if(thisNum - firstNum - rangeSize == 1){
               rangeSize++;
           }else{
               if(rangeSize != 0){
                   result.append(firstNum).append((rangeSize == 1) ? ",": "-")
                           .append(firstNum+rangeSize).append(",");
                   rangeSize = 0;
               }else{
                   result.append(firstNum).append(",");
               }
               firstNum = thisNum;
           }
       }
       if(rangeSize != 0){
           result.append(firstNum).append((rangeSize == 1) ? "," : "-").
                   append(firstNum + rangeSize);
           rangeSize = 0;
       } else {
           result.append(firstNum);
       }
       return result.toString();
   }

}</lang> Output:

-6,-3-1,3-5,7-11,14,15,17-20
0-2,4,6-8,11,12,14-25,27-33,35-39

JavaScript

<lang javascript>function rangeExtraction(list) {

 var len = list.length;
 var out = [];
 var i, j;
 for (i = 0; i < len; i = j + 1) {
   // beginning of range or single
   out.push(list[i]);
   
   // find end of range
   for (var j = i + 1; j < len && list[j] == list[j-1] + 1; j++);
   j--;
   
   if (i == j) {
     // single number
     out.push(",");
   } else if (i + 1 == j) {
     // two numbers
     out.push(",", list[j], ",");
   } else { 
     // range
     out.push("-", list[j], ",");
   }
 }
 out.pop(); // remove trailing comma
 return out.join("");

}

// using print function as supplied by Rhino standalone print(rangeExtraction([

 0,  1,  2,  4,  6,  7,  8, 11, 12, 14,
 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
 25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
 37, 38, 39

]));</lang>

K

<lang k>grp : {(&~1=0,-':x)_ x} fmt : {:[1=#s:$x;s;(*s),:[3>#s;",";"-"],*|s]} erng: {{x,",",y}/,//'fmt'grp x}</lang>

Example: <lang k> erng 0 1 2 4 6 7 8 11 12 14 15 16 17 18 19 20 21 22 23 24 25 27 28 29 30 31 32 33 35 36 37 38 39 "0-2,4,6-8,11,12,14-25,27-33,35-39"</lang>

MUMPS

<lang MUMPS>RANGCONT(X) ;Integer range contraction

NEW Y,I,CONT,NOTFIRST,CURR,PREV,NEXT,SEQ SET Y="",SEQ=0,PREV="",CONT=0
FOR I=1:1:$LENGTH(X,",") DO
.SET NOTFIRST=$LENGTH(Y),CURR=$PIECE(X,",",I),NEXT=$PIECE(X,",",I+1)
.FOR  Q:$EXTRACT(CURR)'=" "  S CURR=$EXTRACT(CURR,2,$LENGTH(CURR))  ;clean up leading spaces
.S SEQ=((CURR-1)=PREV)&((CURR+1)=NEXT)
.IF 'NOTFIRST SET Y=CURR
.IF NOTFIRST DO
..;Order matters due to flags
..IF CONT&SEQ ;Do nothing
..IF 'CONT&'SEQ SET Y=Y_","_CURR
..IF CONT&'SEQ SET Y=Y_CURR,CONT=0
..IF 'CONT&SEQ SET Y=Y_"-",CONT=1
.SET PREV=CURR
IF CONT SET Y=Y_PREV
K I,CONT,NOTFIRST,CURR,PREV,NEXT,SEQ
QUIT Y</lang>

Example:

USER>SET S="0,1,2,4,6,7,8,11,12,14,15,16,17,18,19,20,21,22,23,24,25,27,28,29,30,31,32,33,35,36,37,38,39"
 
USER>W $$RANGCONT^ROSETTA(S)
0-2,4,6-8,11,12,14-25,27-33,35-39

OCaml

<lang ocaml>let range_extract = function

 | [] -> []
 | x::xs ->
   let f (i,j,ret) k =
     if k = succ j then (i,k,ret) else (k,k,(i,j)::ret) in
   let (m,n,ret) = List.fold_left f (x,x,[]) xs in
   List.rev ((m,n)::ret)

let string_of_range rng =

 let str (a,b) =
   if a = b then string_of_int a
   else Printf.sprintf "%d%c%d" a (if b = succ a then ',' else '-') b in
 String.concat "," (List.map str rng)

let () =

 let li =
   [ 0; 1; 2; 4; 6; 7; 8; 11; 12; 14; 15; 16; 17; 18; 19; 20; 21;
     22; 23; 24; 25; 27; 28; 29; 30; 31; 32; 33; 35; 36; 37; 38; 39 ]
 in
 let rng = range_extract li in
 print_endline(string_of_range rng)</lang>

Output

0-2,4,6-8,11,12,14-25,27-33,35-39

Oz

<lang oz>declare

 fun {Extract Xs}
    {CommaSeparated
     {Map {ExtractRanges Xs} RangeToString}}
 end
 fun {ExtractRanges Xs}
    fun {Loop Ys Start End}
       case Ys
       of Y|Yr andthen Y == End+1 then {Loop Yr Start Y}
       [] Y|Yr                    then Start#End|{Loop Yr Y Y} 
       [] nil                     then [Start#End]
       end
    end
 in
    case Xs
    of X|Xr then {Loop Xr X X}
    [] nil then nil
    end
 end
 
 fun {RangeToString S#E}
    if E-S >= 2 then
       {VirtualString.toString S#"-"#E}
    else
       {CommaSeparated
        {Map {List.number S E 1} Int.toString}}
    end
 end
 fun {CommaSeparated Xs}
    {Flatten {Intersperse "," Xs}}
 end
  
 fun {Intersperse Sep Xs}
    case Xs of X|Y|Xr then
       X|Sep|{Intersperse Sep Y|Xr}
    else
       Xs
    end
 end

in

 {System.showInfo
  {Extract [ 0 1 2 4 6 7 8 11 12 14
             15 16 17 18 19 20 21 22 23 24
             25 27 28 29 30 31 32 33 35 36
             37 38 39 ]}}</lang>

Sample output: <lang oz>0-2,4,6-8,11,12,14-25,27-33,35-39</lang>

Perl

Using regexes. Also handles +/- and negative integer ranges.

<lang Perl>sub rangext {

   my $str = join ' ', @_;
   1 while $str =~ s{([+-]?\d+) ([+-]?\d+)}
       {$1.(abs($2 - $1) == 1 ? '~' : ',').$2}eg; # abs for neg ranges
   $str =~ s/(\d+)~(?:[+-]?\d+~)+([+-]?\d+)/$1-$2/g;
   $str =~ tr/~/,/;
   return $str;

}

  1. Test and display

my @test = qw(0 1 2 4 6 7 8 11 12 14,

            15 16 17 18 19 20 21 22 23 24,
            25 27 28 29 30 31 32 33 35 36,
            37 38 39);

print rangext(@test), "\n";</lang>

Output:

0-2,4,6-8,11,12,14-25,27-33,35-39

Perl 6

<lang Perl6>sub range-extraction (@integer-list) {

   my $prev-term = [@integer-list.shift];
   my @range-list;
   
   for @integer-list -> $element {
       if $element == $prev-term[*-1] + 1 {
           $prev-term.push: $element;
       }
       else {
           @range-list.push: $prev-term;
           $prev-term = [$element];
       }
   }
   @range-list.push: $prev-term;
   @range-list.map({$^a.elems>2 ?? $a[0]~'-'~$a[*-1] !! $a.flat}).join(',');

}

say range-extraction(

   (-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20)

);

say range-extraction(

   (0,  1,  2,  4,  6,  7,  8, 11, 12, 14,
   15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
   25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
   37, 38, 39)

);</lang>

Output:

-6,-3-1,3-5,7-11,14,15,17-20
0-2,4,6-8,11,12,14-25,27-33,35-39

PicoLisp

<lang PicoLisp>(de rangeextract (Lst)

  (glue ","
     (make
        (while Lst
           (let (N (pop 'Lst)  M N)
              (while (= (inc M) (car Lst))
                 (setq M (pop 'Lst)) )
              (cond
                 ((= N M) (link N))
                 ((= (inc N) M) (link N M))
                 (T (link (list N '- M))) ) ) ) ) ) )</lang>

Output:

: (rangeextract
   (0 1 2 4 6 7 8 11 12 14 15 16 17 18 19 20 21 22
      23 24 25 27 28 29 30 31 32 33 35 36 37 38 39 ) )

-> "0-2,4,6-8,11,12,14-25,27-33,35-39"

PL/I

This example is incorrect. Please fix the code and remove this message.

Details: Ranges must cover >2 integers.

<lang PL/I> range_extraction:

  procedure options (main);
  declare c character (1);
  declare (old, new, initial) fixed binary (31);
  declare out file output;
  open file (out) output title ('/out,type(text),recsize(70)');
  c = ' ';
  get list (old);
  do forever;
     initial = old;
     on endfile (sysin) begin;
        put file (out) edit (c, trim(old)) (a);
        stop;
     end;
     get (new);
     put edit (c) (a);
     if new = old+1 then
        do; /* we have a run. */
           on endfile (sysin) begin;
              put file (out) edit (c, trim(initial), '-', trim(old) ) (a);
              stop;
           end;
           do while (new = old+1);
              old = new;
              get (new);
           end;
           /* At this point, old holds the last in a run; */
           /* initial holds the first in a run. */
           put file (out) edit (c, trim(initial), '-', trim(old) ) (a);
           old = new;
        end;
     else /* we have an isolated value. */
        do;
           put file (out) edit (c, trim(old)) (a);
           old = new;
        end;
     c = ',';
  end;

end range_extraction; </lang> OUTPUT: <lang>

0-2,4,6-8,11-12,14-25,27-33,35-39

</lang>

Prolog

Works with SWI-Prolog and library clpfd.
The code uses three predicates extract_Range/2, study_Range/2 and pack_Range/2.
Every predicate works in both directions arg1 towards arg2 and arg2 towards arg1, so that Range extraction and Range expansion work with the same predicates but in reverse order. <lang Prolog>range_extract :- L = [0, 1, 2, 4, 6, 7, 8, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39] , writeln(L), pack_Range(L, LP), maplist(study_Range, R, LP), extract_Range(LA, R), atom_chars(A, LA), writeln(A).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % extract_Range(?In, ?Out) % In  : '-6,-3--1,3-5,7-11,14,15,17-20' => % Out : [-6], [-3--1], [3-5],[7-11], [14],[15], [17-20] % extract_Range([], []).


extract_Range(X , [Range | Y1]) :- get_Range(X, U-U, Range, X1), extract_Range(X1, Y1).


get_Range([], Range-[], Range, []). get_Range([','|B], Range-[], Range, B) :- !.

get_Range([A | B], EC, Range, R) :- append_dl(EC, [A | U]-U, NEC), get_Range(B, NEC, Range, R).


append_dl(X-Y, Y-Z, X-Z).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % study Range(?In, ?Out) % In  : [-6] % Out : [-6,-6] % % In  : [-3--1] % Out : [-3, -1] % study_Range(Range1, [Deb, Deb]) :-

      catch(number_chars(Deb, Range1), Deb, false).

study_Range(Range1, [Deb, Fin]) :-

      append(A, ['-'|B], Range1),
      A \= [],
      number_chars(Deb, A),
      number_chars(Fin, B).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %

- use_module(library(clpfd)).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Pack Range(?In, ?Out) % In  : -6, % Out : [-6] % % In  : -3, -2,-1 % Out : [-3,-1] % pack_Range([],[]).

pack_Range([X|Rest],[[X | V]|Packed]):-

   run(X,Rest, [X|V], RRest),
   pack_Range(RRest,Packed).


run(Fin,[Other|RRest], [Deb, Fin],[Other|RRest]):- Fin #\= Deb, Fin #\= Deb + 1, Other #\= Fin+1.

run(Fin,[],[_Var, Fin],[]).

run(Var,[Var1|LRest],[Deb, Fin], RRest):- Fin #\= Deb, Fin #\= Deb + 1, Var1 #= Var + 1, run(Var1,LRest,[Deb, Fin], RRest).

run(Val,[Other|RRest], [Val, Val],[Other|RRest]). </lang>

OutPut :

?- range_extract.
[0,1,2,4,6,7,8,11,12,14,15,16,17,18,19,20,21,22,23,24,25,27,28,29,30,31,32,33,35,36,37,38,39]
0-2,4,6-8,11,12,14-25,27-33,35-39
true

PureBasic

Even though the example integer list only includes ascending ranges this code will also handles descending ranges. <lang PureBasic>DataSection

 Data.i  33 ;count of elements to be read
 Data.i  0,  1,  2,  4,  6,  7,  8, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24
 Data.i  25, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39

EndDataSection

NewList values()

setup list

Define elementCount, i Read.i elementCount For i = 1 To elementCount

 AddElement(values()): Read.i values()

Next

Procedure.s rangeExtract(List values())

 Protected listSize = ListSize(values()) - 1
 Protected rangeMarker, rangeStart, rangeIncrement, retraceSteps, rangeSize, endOfRange, output.s, sub.s
 
 ForEach values()
   rangeStart = values(): 
   sub = Str(rangeStart)
   If NextElement(values())
     retraceSteps = 1
     rangeIncrement = values() - rangeStart
     If rangeIncrement = 1 Or rangeIncrement = -1
       ;found start of possible range
       If ListIndex(values()) <> listSize
         retraceSteps = 2
         rangeSize = 2
         endOfRange = #False
         rangeMarker = values()
         While NextElement(values())
           If values() - rangeMarker <> rangeIncrement
             endOfRange = #True
             Break
           EndIf
           rangeSize + 1
           rangeMarker = values()
         Wend
         
         If rangeSize > 2
           sub = Str(rangeStart) + "-" + Str(rangeMarker)
           If Not endOfRange
             retraceSteps = 0 ;at end of list
           Else
             retraceSteps = 1
           EndIf 
         EndIf
       EndIf 
     EndIf
     
     ;return to the value before look-aheads
     While retraceSteps > 0
       PreviousElement(values()): retraceSteps - 1
     Wend 
   EndIf
   
   output + sub + "," 
 Next
 
 ProcedureReturn RTrim(output, ",")

EndProcedure

If OpenConsole()

 PrintN(rangeExtract(values()))
 
 Print(#CRLF$ + #CRLF$ + "Press ENTER to exit")
 Input()
 CloseConsole()

EndIf</lang> Sample output:

0-2,4,6-8,11,12,14-25,27-33,35-39

Python

<lang python>#import random

def rangeextract(lst):

   lenlst = len(lst)
   i, ranges = 0, []
   while i< lenlst:
       low = lst[i]
       while i <lenlst-1 and lst[i]+1 == lst[i+1]: i +=1
       hi = lst[i]
       ranges.append(
           '%i-%i'  % (low, hi) if hi - low >= 2 else
           ('%i,%i' % (low, hi) if hi - low == 1 else
            '%i' % low) )
       i += 1
   return ','.join(ranges)

lst = [ 0, 1, 2, 4, 6, 7, 8, 11, 12, 14,

      15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
      25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
      37, 38, 39]

print(rangeextract(lst))</lang>

Sample output

0-2,4,6-8,11,12,14-25,27-33,35-39

Qi

<lang qi> (define make-range

 Start Start -> ["," Start]
 Start End   -> ["," Start "," End] where (= End (+ Start 1))
 Start End   -> ["," Start "-" End])

(define range-extract-0

 Start End []     -> (make-range Start End)
 Start End [A|As] -> (range-extract-0 Start A As) where (= (+ 1 End) A)
 Start End [A|As] -> (append (make-range Start End) (range-extract-0 A A As)))

(define range-extract

 [A |As] -> (FORMAT NIL "~{~a~}" (tail (range-extract-0 A A As))))

(range-extract [ 0 1 2 4 6 7 8 11 12 14

               15 16 17 18 19 20 21 22 23 24
               25 27 28 29 30 31 32 33 35 36
               37 38 39])

</lang>

Output:

0-2,4,6-8,11,12,14-25,27-33,35-39

REXX

<lang> /*REXX program to test range extraction. */

aaa='0 1 2 4 6 7 8 11 12 14 15 16 17 18 19 20 21 22',

   '23 24 25 27 28 29 30 31 32 33 35 36 37 38 39'

say 'old='aaa /*show the old range of numbers. */ say new= /*the new list (maybe with ranges)*/ w=words(aaa) /*number of numbers in the list. */

                 /*Note: index of DO  (J)  gets modified within loop !.*/
 do j=1 to w                         /*step through  num  word in list.*/
 _=word(aaa,j)                       /*get the Jth number in the list. */
 new=new',' _                        /*append  Jth number to new list. */
 inc=1                               /*start with an increment of one. */
   do k=j+1 to w                     /*now, search for end of range.   */
   __=word(aaa,k)                    /*get the Kth number in the list. */
   if __\==_+inc then leave          /*this number 1 greater than prev?*/
   inc=inc+1                         /*yes, then increase the range.   */
   g_=__                             /*placeholder for last good number*/
   end
 k=k-1                               /*fudge the Kth word (subtract 1).*/
 if k==j then iterate                /*no range?    Then keep truckin'.*/
 if g_==_+1 then iterate             /*range of 1?  Then keep truckin'.*/
 new=new'-'g_                        /*indicate a range of numbers.    */
 j=k                                 /*Bad practice!!  Change DO index.*/
 end   /*j*/

new=substr(new,3) /*remove extraneous leading comma.*/ new=space(new,0) /*remove all spaces (blanks). */ say 'new='new /*display the new list of numbers.*/ </lang> Output (note that 11 and 12 are not considered a range):

old=0 1 2 4 6 7 8 11 12 14 15 16 17 18 19 20 21 22 23 24 25 27 28 29 30 31 32 33 35 36 37 38 39

new=0-2,4,6-8,11,12,14-25,27-33,35-39

Ruby

<lang ruby>def range_extract(l)

 sorted = l.sort
 range = []
 start = sorted.first
 # pad the list with a big value, so that the last loop iteration will 
 # appended something to the range
 sorted.concat([Float::MAX]).each_cons(2) do |prev,n|
   if prev.succ < n
     if start == prev
       range << start.to_s
     else
       range << "%d%s%d" % [start, (start.succ == prev ? "," : "-"), prev]
     end
     start = n
   end
 end
 range.join(',')

end

lst = [

   0,  1,  2,  4,  6,  7,  8, 11, 12, 14,
  15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
  25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
  37, 38, 39

]

p rng = range_extract(lst)</lang>

output:

"0-2,4,6-8,11,12,14-25,27-33,35-39"

Scala

<lang scala>object Range {

  def spanRange(ls:List[Int])={
    var last=ls.head
    ls span {x => val b=x<=last+1; last=x; b}
  }
  def toRangeList(ls:List[Int]):List[List[Int]]=ls match {
     case Nil => List()
     case _ => spanRange(ls) match {
        case (range, Nil) => List(range)
        case (range, rest) => range :: toRangeList(rest)
     }
  }
  def toRangeString(ls:List[List[Int]])=ls map {r=>
     if(r.size<3) r mkString ","
     else r.head + "-" + r.last
  } mkString ","
  def main(args: Array[String]): Unit = {
     var l=List(0, 1, 2, 4, 6, 7, 8, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
                27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39)
     println(toRangeString(toRangeList(l)))
  }

}</lang>

Output:

0-2,4,6-8,11,12,14-25,27-33,35-39

Scheme

Translation of: Qi

<lang scheme> (define (make-range start end)

 (cond ((= start end)
        `("," ,start))
       ((= end (+ start 1))
        `("," ,start "," ,end))
       (else
        `("," ,start "-" ,end))))

(define (range-extract-0 start end a)

 (cond ((null? a)
        (make-range start end))
       ((= (+ 1 end) (car a))
        (range-extract-0 start (car a) (cdr a)))
       (else
        (append (make-range start end)
                (range-extract-0 (car a) (car a) (cdr a))))))

(define (range-extract a)

 (apply string-append (map (lambda (x)
                             (if (number? x)
                                 (number->string x)
                                 x))
                           (cdr (range-extract-0 (car a) (car a) (cdr a))))))

(range-extract '( 0 1 2 4 6 7 8 11 12 14

                15 16 17 18 19 20 21 22 23 24
                25 27 28 29 30 31 32 33 35 36
                37 38 39))

</lang>

Outputs:

0-2,4,6-8,11,12,14-25,27-33,35-39

Seed7

<lang seed7>$ include "seed7_05.s7i";

const func string: rangeExtraction (in array integer: numbers) is func

 result
   var string: rangeStri is "";
 local
   var integer: index is 1;
   var integer: index2 is 1;
 begin
   while index <= length(numbers) do
     while index2 <= pred(length(numbers)) and numbers[succ(index2)] = succ(numbers[index2]) do
       incr(index2);
     end while;
     if succ(index) < index2 then
       rangeStri &:= "," <& numbers[index] <& "-" <& numbers[index2];
     else
       while index <= index2 do
         rangeStri &:= "," <& numbers[index];
         incr(index);

end while;

     end if;
     incr(index2);
     index := index2;
   end while;
   rangeStri := rangeStri[2 ..];
 end func;

const proc: main is func

 begin
   writeln(rangeExtraction([] (0, 1, 2, 4, 6, 7, 8, 11, 12, 14, 15, 16, 17, 18, 19,
       20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39)));
 end func;</lang>

Output:

0-2,4,6-8,11,12,14-25,27-33,35-39

SNOBOL4

Translation of: Perl
Works with: Macro Spitbol
Works with: CSnobol

Handles +/- and negative ranges.

<lang SNOBOL4>* # Absolute value

       define('abs(n)') :(abs_end)

abs abs = ~(abs = lt(n,0) -n) n :(return) abs_end

       define('rangext(str)d1,d2') :(rangext_end)

rangext num = ('+' | '-' | ) span('0123456789') rxt1 str ',' span(' ') = ' ' :s(rxt1) rxt2 str num . d1 ' ' num . d2 = + d1 ('~,' ? *eq(abs(d2 - d1),1) '~' | ',') d2 :s(rxt2) rxt3 str ('~' | '-') num '~' = '-' :s(rxt3) rxt4 str '~' = ',' :s(rxt4)

       rangext = str :(return)

rangext_end

  • # Test and display
       test =  '0,  1,  2,  4,  6,  7,  8, 11, 12, 14, '

+ '15, 16, 17, 18, 19, 20, 21, 22, 23, 24, ' + '25, 27, 28, 29, 30, 31, 32, 33, 35, 36, ' + '37, 38, 39'

       output = rangext(test)

end</lang>

Output:

0-2,4,6-8,11,12,14-25,27-33,35-39

Tcl

<lang tcl>proc rangeExtract list {

   set result [lindex $list 0]
   set first [set last [lindex $list 0]]
   foreach term [lrange $list 1 end] {

if {$term == $last+1} { set last $term continue } if {$last > $first} { append result [expr {$last == $first+1 ? "," : "-"}] $last } append result "," $term set first [set last $term]

   }
   if {$last == $first+1} {

append result "," $last

   } elseif {$last > $first} {

append result "-" $last

   }
   return $result

}

  1. Commas already removed so it is a natural Tcl list

puts [rangeExtract {

   0 1 2 4 6 7 8 11 12 14
   15 16 17 18 19 20 21 22 23 24
   25 27 28 29 30 31 32 33 35 36
   37 38 39

}]</lang> Output:

0-2,4,6-8,11,12,14-25,27-33,35-39

TUSCRIPT

TUSCRIPT has a built-in routine "COMBINE" that combines a range of integers by a dash '-'. It is possible to differ between every range that expands more than two values (6-8), and every range that expands less than two values (11,12 are not combined). <lang tuscript> $$ MODE TUSCRIPT MODE DATA $$ numbers=* 0, 1, 2, 4, 6, 7, 8, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39 $$ MODE TUSCRIPT numbers=EXCHANGE (numbers,":,><<> :':") unrangednrs=JOIN (numbers,"") rangednrs=COMBINE (unrangednrs,"") rangednrs=EXCHANGE (rangednrs,":':,:") PRINT rangednrs </lang> Output:

0-2,4,6-8,11,12,14-25,27-33,35-39

Solution without COMBINE <lang tuscript> $$ MODE TUSCRIPT MODE DATA $$ numbers=* 0, 1, 2, 4, 6, 7, 8, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39 $$ MODE TUSCRIPT numbers=EXCHANGE (numbers,":,><<> :':") unrangednrs=JOIN (numbers,"")

help = APPEND (unrangednrs, "999999999") rest = REMOVE (help, 1, n_1) n_2 = n_1, n_3= n_2 + 1,rangednrs= "" LOOP n= rest

IF (n!=n_3)  THEN
   rangednrs = APPEND (rangednrs, n_1)
   IF (n_1!=n_2) THEN
   range=n_1+1
     IF (range==n_2) THEN
     rangednrs = APPEND (rangednrs,n_2)
     ELSE
     rangednrs = CONCAT (rangednrs, "-", n_2)
     ENDIF
   ENDIF
   n_1 = n
ENDIF
n_2 = n, n_3 = n_2 + 1

ENDLOOP rangednrs=EXCHANGE (rangednrs,":':,:") PRINT rangednrs


</lang> Output:

0-2,4,6-8,11,12,14-25,27-33,35-39

Ursala

<lang Ursala>#import std

  1. import int

x = <0,1,2,4,6,7,8,11,12,14,15,16,17,18,19,20,21,22,23,24,25,27,28,29,30,31,32,33,35,36,37,38,39>

f = mat`,+ ==?(~&l,^|T/~& :/`-)*bhPS+ %zP~~hzX*titZBPiNCSiNCQSL+ rlc ^|E/~& predecessor

  1. show+

t = <f x></lang>

output:

0-2,4,6-8,11,12,14-25,27-33,35-39

VBA

<lang vb> Public Function RangeExtraction(AList) As String 'AList is a variant that is an array, assumed filled with numbers in ascending order Const RangeDelim = "-" 'range delimiter Dim result As String Dim InRange As Boolean Dim Posn, ub, lb, rangestart, rangelen As Integer

result = "" 'find dimensions of AList ub = UBound(AList) lb = LBound(AList) Posn = lb While Posn < ub

 rangestart = Posn
 rangelen = 0
 InRange = True
 'try to extend the range
 While InRange
   rangelen = rangelen + 1
   If Posn = ub Then
     InRange = False
   Else
     InRange = (AList(Posn + 1) = AList(Posn) + 1)
     Posn = Posn + 1
   End If
 Wend
 If rangelen > 2 Then 'output the range if it has more than 2 elements
   result = result & "," & Format$(AList(rangestart)) & RangeDelim & Format$(AList(rangestart + rangelen - 1))
 Else 'output the separate elements
   For i = rangestart To rangestart + rangelen - 1
     result = result & "," & Format$(AList(i))
   Next
 End If
 Posn = rangestart + rangelen

Wend RangeExtraction = Mid$(result, 2) 'get rid of first comma! End Function


Public Sub RangeTest() 'test function RangeExtraction 'first test with a Variant array Dim MyList As Variant MyList = Array(0, 1, 2, 4, 6, 7, 8, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39) Debug.Print "a) "; RangeExtraction(MyList)

'next test with an array of integers Dim MyOtherList(1 To 20) As Integer MyOtherList(1) = -6 MyOtherList(2) = -3 MyOtherList(3) = -2 MyOtherList(4) = -1 MyOtherList(5) = 0 MyOtherList(6) = 1 MyOtherList(7) = 3 MyOtherList(8) = 4 MyOtherList(9) = 5 MyOtherList(10) = 7 MyOtherList(11) = 8 MyOtherList(12) = 9 MyOtherList(13) = 10 MyOtherList(14) = 11 MyOtherList(15) = 14 MyOtherList(16) = 15 MyOtherList(17) = 17 MyOtherList(18) = 18 MyOtherList(19) = 19 MyOtherList(20) = 20 Debug.Print "b) "; RangeExtraction(MyOtherList) End Sub </lang>

Output:

RangeTest
a) 0-2,4,6-8,11,12,14-25,27-33,35-39
b) -6,-3-1,3-5,7-11,14,15,17-20