Range extraction: Difference between revisions

m
→‎{{header|Ring}}: added RPL header
(→‎{{header|Python}}: Hopefully strikes a balance between the three replaced versions w.r.t wanting/not wanting if/else expressions and needing access to a non-string version of the ranges and separates the range extraction from its printing.)
m (→‎{{header|Ring}}: added RPL header)
(252 intermediate revisions by 81 users not shown)
Line 2:
{{:Range extraction/Format}}
 
;Task:
'''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. (The correct answer is: <code>0-2,4,6-8,11,12,14-25,27-33,35-39</code>).
<br>
0, 1, 2, 4, 6, 7, 8, 11, 12, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
Line 11 ⟶ 12:
* Show the output of your program.
 
 
C.f. [[Range expansion]]
;Related task:
* &nbsp; [[Range expansion]]
<br><br>
 
=={{header|11l}}==
{{trans|Python}}
 
<syntaxhighlight lang="11l">F range_extract(lst)
[[Int]] r
V lenlst = lst.len
V i = 0
L i < lenlst
V low = lst[i]
L i < lenlst - 1 & lst[i] + 1 == lst[i + 1]
i++
V hi = lst[i]
I hi - low >= 2
r [+]= [low, hi]
E I hi - low == 1
r [+]= [low]
r [+]= [hi]
E
r [+]= [low]
i++
R r
 
F printr(ranges)
print(ranges.map(r -> (I r.len == 2 {r[0]‘-’r[1]} E String(r[0]))).join(‘,’))
 
L(lst) [[-8, -7, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7,
8, 9, 10, 11, 14, 15, 17, 18, 19, 20],
[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]]
printr(range_extract(lst))</syntaxhighlight>
 
{{out}}
<pre>
-8--6,-3-1,3-5,7-11,14,15,17-20
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
=={{header|Action!}}==
<syntaxhighlight lang="action!">INT FUNC FindRange(INT ARRAY a INT len,start)
INT count
count=1
WHILE start<len-1
DO
IF a(start)+1#a(start+1) THEN
EXIT
FI
start==+1
count==+1
OD
RETURN (count)
 
PROC Append(CHAR ARRAY text,suffix)
BYTE POINTER srcPtr,dstPtr
BYTE len
 
len=suffix(0)
IF text(0)+len>255 THEN
len=255-text(0)
FI
IF len THEN
srcPtr=suffix+1
dstPtr=text+text(0)+1
MoveBlock(dstPtr,srcPtr,len)
text(0)==+suffix(0)
FI
RETURN
 
PROC RangeToStr(INT ARRAY a INT len CHAR ARRAY res)
INT i,count
CHAR ARRAY tmp(10)
 
i=0
res(0)=0
WHILE i<len
DO
count=FindRange(a,len,i)
StrI(a(i),tmp) Append(res,tmp)
IF count=2 THEN
Append(res,",")
StrI(a(i+1),tmp) Append(res,tmp)
ELSEIF count>2 THEN
Append(res,"-")
StrI(a(i+count-1),tmp) Append(res,tmp)
FI
i==+count
IF i<len THEN
Append(res,",")
FI
OD
RETURN
 
PROC Main()
INT ARRAY a=[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]
INT ARRAY b=[65530 65533 65534 65535
0 1 3 4 5 7 8 9 10 11 14 15 17 18 19 20]
CHAR ARRAY res(256)
 
RangeToStr(a,33,res)
PrintE(res) PutE()
RangeToStr(b,20,res)
PrintE(res)
RETURN</syntaxhighlight>
{{out}}
[https://gitlab.com/amarok8bit/action-rosetta-code/-/raw/master/images/Range_extraction.png Screenshot from Atari 8-bit computer]
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39
 
-6,-3-1,3-5,7-11,14,15,17-20
</pre>
 
=={{header|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'''.
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.
the iterative solution uses Unbounded_Strings.
 
<langsyntaxhighlight Adalang="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;
Line 66 ⟶ 185:
37, 38, 39
) ) );
end Range_Extraction;</langsyntaxhighlight>
 
 
Line 73 ⟶ 192:
The recursive solution avoids the usage of unbounded strings.
 
<langsyntaxhighlight Adalang="ada">with Ada.Text_IO, Ada.Strings.Fixed;
 
procedure Range_Extract is
Line 117 ⟶ 236:
17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 29,
30, 31, 32, 33, 35, 36, 37, 38, 39) ));
end Range_Extract;</langsyntaxhighlight>
 
===Sample output===
 
{{out}}
The sample output is exactly the same, for both solutions:
 
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|Aime}}==
 
<syntaxhighlight lang="aime">rp(list l)
<lang aime>void
fs(integer &s, data b)
{
if (s) {
b_append(b, ',');
}
s = 1;
}
 
text
rp(...)
{
integer fa, i, j;
data b;
index x;
 
fa = l[0];
ix[a] = 0a;
jfor =(, 0;a in l) {
x[a == x.back + 1 ? x.high : a] = a;
while (i < count()) {
}
while (j < count() - 1) {
for (i, a in x) {
if (__integer(lead(j + 1)) == __integer(lead(j)) + 1) {
b.form(a - i < 2 ? a - i ? "~,~," : "~," : "~-~,", i, a);
j += 1;
} else {
break;
}
}
if (i + 1 < j) {
fs(f, b);
b_suffix(b, itoa(lead(i)));
b_append(b, '-');
b_suffix(b, itoa(lead(j)));
} else {
while (i < j + 1) {
fs(f, b);
b_suffix(b, itoa(lead(i)));
i += 1;
}
}
j += 1;
i = j;
}
 
return b_string(b.delete(-1);
}
 
integer
main(void)
{
o_texto_(rp(list(0, 1, 2, 4, 6, 7, 8, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
23, 24, 25, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39));,
o_byte(' "\n'");
 
return 0;
}</langsyntaxhighlight>
 
;Output:
 
{{out}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|ALGOL 68}}==
Note: The following [[Range extraction#Iterative|Iterative]] code specimen is the "unrolled" version of the [[Range extraction#Generative|Generative]] code specimen below. Together they provided as a comparison of the two different methods.
 
=== Iterative ===
{{works with|ALGOL 68|Revision 1 - one minor extension to language used - PRAGMA READ, similar to C's #include directive.}}
Line 194 ⟶ 283:
* The closest concept that ''Algol 68'' has to [[wp:duck typing|duck typing]] is the [[wp:tagged union|tagged union]]. This is used to define '''mode''' '''urange''' = '''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 <i>Algol 68</i>'s strong type ''data security''.
'''File: Template_Range_extraction_Base.a68'''
<langsyntaxhighlight lang="algol68">###
REQUIRES(MODE SCALAR, OP(SCALAR,SCALAR)BOOL =, OP(SCALAR,SCALAR)SCALAR +);
###
Line 247 ⟶ 336:
);
 
OP (UNIRANGELIST)STRING REPR = unirange list repr; # alias #</langsyntaxhighlight>'''File: Template_Range_extraction_Iterative.a68'''
<langsyntaxhighlight lang="algol68">###
REQUIRES(MODE SCALAR, OP(SCALAR,SCALAR)BOOL =, OP(SCALAR,SCALAR)SCALAR +);
###
Line 307 ⟶ 396:
 
out unirange list[:upb out unirange list]
);</langsyntaxhighlight>'''File: test_Range_extraction_Integer.a68'''<langsyntaxhighlight lang="algol68">#!/usr/local/bin/a68g --script #
############################
# some simple test cases: #
Line 350 ⟶ 439:
REPR list c
))
END</langsyntaxhighlight>
 
'''Output:'''
{{out}}
<pre style="height:15ex;overflow:scroll">
0-2,4,6-8,11,12,14-25,27-33,35-39
Line 372 ⟶ 462:
# Finally iterate with ''unirange list init'' '''exiting''' with an array of '''union''' of '''int''' and '''range'''.
'''File: Template_Range_extraction_Generative.a68'''
<langsyntaxhighlight lang="algol68">###
REQUIRES(MODE SCALAR, OP(SCALAR,SCALAR)BOOL =, OP(SCALAR,SCALAR)SCALAR +);
###
Line 454 ⟶ 544:
);
 
OP (UNIRANGELISTS)UNIRANGELIST INITUNIRANGE = unirange list init; # alias #</langsyntaxhighlight>
{{out}}
'''Output:'''
<pre style="height:15ex;overflow:scroll">
0-2,4,6-8,11,12,14-25,27-33,35-39
Line 463 ⟶ 553:
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
=={{header|AppleScript}}==
===Functional===
{{Trans|JavaScript}}
<syntaxhighlight lang="applescript">--------------------- RANGE EXTRACTION ---------------------
 
-- rangeFormat :: [Int] -> String
on rangeFormat(xs)
script segment
on |λ|(xs)
if 2 < length of xs then
intercalate("-", {first item of xs, last item of (xs)})
else
intercalate(",", xs)
end if
end |λ|
end script
script gap
on |λ|(a, b)
1 < b - a
end |λ|
end script
intercalate(",", map(segment, splitBy(gap, xs)))
end rangeFormat
 
 
--------------------------- TEST ---------------------------
on run
set xs to {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}
rangeFormat(xs)
--> "0-2,4,6-8,11,12,14-25,27-33,35-39"
end run
 
 
-------------------- GENERIC FUNCTIONS ---------------------
 
 
-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
tell mReturn(f)
set v to startValue
set lng to length of xs
repeat with i from 1 to lng
set v to |λ|(v, item i of xs, i, xs)
end repeat
return v
end tell
end foldl
 
 
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
tell mReturn(f)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map
 
 
-- intercalate :: Text -> [Text] -> Text
on intercalate(strText, lstText)
set {dlm, my text item delimiters} to {my text item delimiters, strText}
set strJoined to lstText as text
set my text item delimiters to dlm
return strJoined
end intercalate
 
 
-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: Handler -> Script
on mReturn(f)
if class of f is script then
f
else
script
property |λ| : f
end script
end if
end mReturn
 
 
-- splitBy :: (a -> a -> Bool) -> [a] -> [[a]]
on splitBy(f, xs)
set mf to mReturn(f)
if length of xs < 2 then
{xs}
else
script p
on |λ|(a, x)
set {acc, active, prev} to a
if mf's |λ|(prev, x) then
{acc & {active}, {x}, x}
else
{acc, active & x, x}
end if
end |λ|
end script
set h to item 1 of xs
set lstParts to foldl(p, {{}, {h}, h}, items 2 thru -1 of xs)
item 1 of lstParts & {item 2 of lstParts}
end if
end splitBy</syntaxhighlight>
{{Out}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
===Straightforward===
 
<syntaxhighlight lang="applescript">(*
The task description doesn't explicitly state that the integers are unique or what to do if they're not.
This script treats runs of equal integers as single instances of those integers.
*)
 
on rangeDescription(orderedListOfIntegers)
script o
property lst : orderedListOfIntegers
property entries : {}
on addEntry(startInt, endInt)
set rangeDifference to endInt - startInt
if (rangeDifference > 1) then
set end of my entries to (startInt as text) & "-" & endInt
else if (rangeDifference is 1) then
set end of my entries to (startInt as text) & "," & endInt
else
set end of my entries to startInt
end if
end addEntry
end script
-- if ((orderedListOfIntegers's class is not list) or ((count o's lst's integers) < (count orderedListOfIntegers))) then error
-- Work through the list, identifying gaps in the sequence and adding range or individual results to o's entries.
set startInt to beginning of o's lst
set endInt to startInt
repeat with i from 2 to (count orderedListOfIntegers)
set thisInt to item i of o's lst
if (thisInt - endInt > 1) then
tell o to addEntry(startInt, endInt)
set startInt to thisInt
end if
set endInt to thisInt
end repeat
tell o to addEntry(startInt, thisInt)
-- Coerce the entries list to text using a comma delimiter.
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to ","
set output to o's entries as text
set AppleScript's text item delimiters to astid
return output
end rangeDescription
 
-- Test code:
set listOfIntegers to {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}
return rangeDescription(listOfIntegers)</syntaxhighlight>
 
{{output}}
<syntaxhighlight lang="applescript">"0-2,4,6-8,11,12,14-25,27-33,35-39"</syntaxhighlight>
 
=={{header|Arturo}}==
 
<syntaxhighlight lang="arturo">extractRange: function [inp][
items: map split.by:"," join split.lines strip inp 'x -> to :integer strip x
ranges: []
 
i: 0
while [i < size items][
fst: items\[i]
offset: i
while [true][
if (i + 1) >= size items -> break
if (fst - offset) <> items\[i+1] - (i+1) -> break
i: i + 1
]
lst: items\[i]
case [(lst-fst)=]
when? -> 0 -> 'ranges ++ ~"|fst|"
when? -> 1 -> 'ranges ++ ~"|fst|, |lst|"
else -> 'ranges ++ ~"|fst|-|lst|"
i: i + 1
]
 
return join.with:", " ranges
]
 
print 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
}</syntaxhighlight>
 
{{out}}
 
<pre>0-2, 4, 6-8, 11, 12, 14-25, 27-33, 35-39</pre>
 
=={{header|AutoHotkey}}==
<langsyntaxhighlight AutoHotkeylang="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 ) {
Line 475 ⟶ 773:
}
return SubStr(ret (f!=p ? (p>f+1 ? "-" : ",") p : ""), 2)
}</langsyntaxhighlight>
{{out}}
Output:<pre>---------------------------
<pre>---------------------------
Range extraction.ahk
---------------------------
Line 486 ⟶ 785:
=={{header|AWK}}==
 
AWK is a primitive bird thethat prefers global scope for arrays.
 
Local variables for functions are declared in the parameters and, by convention, separated from the expected ones by extra space.
by convention, separated from the expected ones by extra space.
 
<langsyntaxhighlight lang="awk">#!/usr/bin/awk -f
 
BEGIN {
Line 543 ⟶ 843:
sequence[s] = a[s]
}
}</langsyntaxhighlight>
 
{{out}}
Sample output:
<pre>
Sequence: 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
Ranges: 0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
=={{header|BASIC}}==
==={{header|BASIC256}}===
{{trans|FreeBASIC}}
<syntaxhighlight lang="basic">arraybase 1
dim a = {-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20}
print formatRange(a)
print
 
dim b = {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 formatRange(b)
end
 
function formatRange (a)
lb = a[?,]: ub = a[?]
if ub = - 1 then return ""
if lb = ub then return string(a[lb])
rangeCount = 1
range = string(a[lb])
for i = lb + 1 to ub
if a[i] = a[i - 1] + 1 then
rangeCount += 1
else
if rangeCount = 1 then
range += "," + string(a[i])
else
if rangeCount = 2 then
rangeCount = 1
range += "," + string(a[i-1]) + "," + string(a[i])
else
rangeCount = 1
range += "-" + string(a[i-1]) + "," + string(a[i])
end if
end if
end if
next
if rangeCount = 2 then
range += "," + string(a[ub])
else
if rangeCount > 2 then range += "-" + string(a[ub])
end if
return range
end function</syntaxhighlight>
{{out}}
<pre>Same as FreeBASIC entry.</pre>
 
==={{header|BBC BASIC}}===
{{works with|BBC BASIC for Windows}}
<langsyntaxhighlight lang="bbcbasic"> range$ = " 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, " + \
Line 576 ⟶ 923:
ENDIF
UNTIL i% = 0
= LEFT$(t$)</langsyntaxhighlight>
{{out}}
'''Output:'''
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
==={{header|FreeBASIC}}===
<syntaxhighlight lang="freebasic">' FB 1.05.0 Win64
 
Function formatRange (a() As Integer) As String
Dim lb As Integer = LBound(a)
Dim ub As Integer = UBound(a)
If ub = - 1 Then Return ""
If lb = ub Then Return Str(a(lb))
Dim rangeCount As Integer = 1
Dim range As String = Str(a(lb))
For i As Integer = lb + 1 To ub
If a(i) = a(i - 1) + 1 Then
rangeCount += 1
ElseIf rangeCount = 1 Then
range += "," + Str(a(i))
ElseIf rangeCount = 2 Then
rangeCount = 1
range += "," + Str(a(i-1)) + "," + Str(a(i))
Else
rangeCount = 1
range += "-" + Str(a(i-1)) + "," + Str(a(i))
End If
Next
If rangeCount = 2 Then
range += "," + Str(a(ub))
ElseIf rangeCount > 2 Then
range += "-" + Str(a(ub))
End If
Return range
End Function
 
Dim a(1 To 20) As Integer = {-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20}
Print formatRange(a())
Print
 
Dim b(1 To 33) As Integer => _
{ _
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 formatRange(b())
Print
Print "Press any key to continue"
Sleep</syntaxhighlight>
 
{{out}}
<pre>
-6,-3-1,3-5,7-11,14,15,17-20
 
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
==={{header|Gambas}}===
'''[https://gambas-playground.proko.eu/?gist=49f362e3de9725fbf3c56f2381abf8a4 Click this link to run this code]'''
<syntaxhighlight lang="gambas">siInput As New Short[]
siInput1 As Short[] = [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]
siInput2 As Short[] = [-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]
sOutput As New String[]
siCount As Short
siNum As Short
'__________________
Public Sub Main()
Dim siLoop As Short
 
For siLoop = 0 To 1
If siLoop = 0 Then siInput = siInput1.Copy() Else siInput = siInput2.Copy()
siCount = 0
siNum = 0
Repeat
If siInput[siCount + 1] = siInput[siCount] + 1 Then
Inc siCount
Else
GetOutput
Endif
Until siCount = siInput.Max
GetOutput
Print sOutput.join(", ")
sOutput.clear
Next
 
End
'__________________
Public Sub GetOutput()
 
If siNum = siCount Then
sOutput.add(siInput[siNum])
Inc siCount
siNum = siCount
End If
 
If siNum <> siCount Then
If siNum = siCount - 1 Then
sOutput.add(siInput[siNum])
sOutput.add(siInput[siNum + 1])
siCount += 2
siNum += 2
Return
End If
sOutput.Add(siInput[siNum] & "-" & siInput[siCount])
Inc siCount
siNum = siCount
End If
 
End</syntaxhighlight>
Output:
<pre>
0-2, 4, 6-8, 11, 12, 14-25, 27-33, 35-39
-6, -3-1, 3-5, 7-11, 14, 15, 17-20
</pre>
 
==={{header|Liberty BASIC}}===
{{works with|Just BASIC}}
{{works with|Run BASIC}}
<syntaxhighlight lang="lb">
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"
print ExtractRange$( s$)
end
 
function ExtractRange$( range$)
n = 1
count = ItemCount( range$, ",")
while n <= count
startValue = val( word$( range$, n, ","))
m = n + 1
while m <= count
nextValue = val( word$( range$, m, ","))
if nextValue - startValue <> m - n then exit while
m = m + 1
wend
if m - n > 2 then
ExtractRange$ = ExtractRange$ + str$( startValue) + "-" + str$( startValue + m - n - 1) + ","
else
for i = n to m - 1
ExtractRange$ = ExtractRange$ + str$( startValue + i - n) + ","
next i
end if
n = m
wend
ExtractRange$ = left$( ExtractRange$, len( ExtractRange$) - 1)
end function
 
function ItemCount( list$, separator$)
while word$( list$, ItemCount + 1, separator$) <> ""
ItemCount = ItemCount + 1
wend
end function
</syntaxhighlight>
{{out}}
<pre> 0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
==={{header|OxygenBasic}}===
<syntaxhighlight lang="oxygenbasic">
int ints(100)
 
ints={
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
}
 
 
' RESULT:
' 0-2, 4, 6-8, 11, 12, 14-25, 27-33, 35-39
 
 
function Ranges(int*i) as string
'===============================
string pr=""
int n=0
int e=0
int j=0
int k=0
int f=1
do
j++
n=i(j)
e=i(j+1)
if e<j
exit do
endif
if e=n+1 and i(j+2)=n+2 then 'LOOKAHEAD
if f then k=n : f=0
else
if f=0 then
pr+=k "-" i(j+1) ", " 'RANGE OF VALUES
j++
f=1
else
pr+=n ", " 'SINGLE VALUES
end if
end if
loop
return left pr, len(pr)-2
end function
print Ranges ints
</syntaxhighlight>
 
==={{header|PureBasic}}===
Even though the example integer list only includes ascending ranges
this code will also handles descending ranges.
<syntaxhighlight 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</syntaxhighlight>
{{out}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
==={{header|QuickBASIC}}===
For negative numbers the results look rather strange, but they satisfy the requirements.
{{trans|BBC BASIC}}
<syntaxhighlight lang="qbasic">
REM Range extraction
DECLARE FUNCTION RangeExtract$ (RS$)
 
Range$ = " 0, 1, 2, 4, 6, 7, 8, 11, 12, 14, "
Range$ = Range$ + "15, 16, 17, 18, 19, 20, 21, 22, 23, 24, "
Range$ = Range$ + "25, 27, 28, 29, 30, 31, 32, 33, 35, 36, "
Range$ = Range$ + "37, 38, 39"
PRINT RangeExtract$(Range$)
END
 
FUNCTION RangeExtract$ (RS$)
TS$ = "": I% = 0
F% = VAL(RS$)
DO
I% = INSTR(I% + 1, RS$, ",")
T% = VAL(MID$(RS$, I% + 1))
IF T% = F% + R% + 1 THEN
R% = R% + 1
ELSE
SELECT CASE R%
CASE 0
TS$ = TS$ + LTRIM$(STR$(F%)) + ","
CASE 1
TS$ = TS$ + LTRIM$(STR$(F%)) + "," + LTRIM$(STR$(F% + R%)) + ","
CASE ELSE
TS$ = TS$ + LTRIM$(STR$(F%)) + "-" + LTRIM$(STR$(F% + R%)) + ","
END SELECT
R% = 0: F% = T%
END IF
LOOP WHILE I% <> 0
RangeExtract$ = LEFT$(TS$, LEN(TS$) - 1)
END FUNCTION
</syntaxhighlight>
{{out}}
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
==={{header|VBA}}===
<syntaxhighlight 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
</syntaxhighlight>
{{out}}
<pre>
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
</pre>
 
==={{header|VBScript}}===
<syntaxhighlight lang="vb">Function Range_Extraction(list)
num = Split(list,",")
For i = 0 To UBound(num)
startnum = CInt(num(i))
sum = startnum
Do While i <= UBound(num)
If sum = CInt(num(i)) Then
If i = UBound(num) Then
If startnum <> CInt(num(i)) Then
If startnum + 1 = CInt(num(i)) Then
Range_Extraction = Range_Extraction & startnum & "," & num(i) & ","
Else
Range_Extraction = Range_Extraction & startnum & "-" & num(i) & ","
End If
Else
Range_Extraction = Range_Extraction & startnum & ","
End If
Exit Do
Else
i = i + 1
sum = sum + 1
End If
Else
If startnum = CInt(num(i-1)) Then
Range_Extraction = Range_Extraction & startnum & ","
Else
If startnum + 1 = CInt(num(i-1)) Then
Range_Extraction = Range_Extraction & startnum & "," & num(i-1) & ","
Else
Range_Extraction = Range_Extraction & startnum & "-" & num(i-1) & ","
End If
End If
i = i - 1
Exit Do
End If
Loop
Next
Range_Extraction = Left(Range_Extraction,Len(Range_Extraction)-1)
End Function
WScript.StdOut.Write 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")</syntaxhighlight>
{{out}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|Bracmat}}==
<langsyntaxhighlight lang="bracmat"> ( rangeExtract
= accumulator firstInRange nextInRange
, accumulate fasten rangePattern
Line 633 ⟶ 1,430:
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
37, 38, 39)</langsyntaxhighlight>
{{out}}
Output:
<pre>(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</pre>
 
=={{header|C}}==
Using the fine tradition of <code>snprintf</code>, <code>rprint</code> 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.
It prints the range only if supplied a non-null pointer,
<lang c>#include <stdio.h>
but always returns the output length sans the terminating null,
so caller can allocate buffer.
<syntaxhighlight lang="c">#include <stdio.h>
#include <stdlib.h>
 
Line 675 ⟶ 1,475:
 
return 0;
}</syntaxhighlight>
}</lang>output<lang>0-2,4,6-8,11,12,14-25,27-33,35-39</lang>
{{out}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|C sharp}}==
<syntaxhighlight 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 if (range.Count() == 2)
{
yield return range.Min() + "," + range.Max();
}
else
{
yield return range.Min() + "-" + range.Max();
}
}
}
}
</syntaxhighlight>
 
{{out}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
===C#: Alternate Version===
<syntaxhighlight lang="csharp">using System;
using System.Collections.Generic;
using System.Linq;
 
public class RangeExtraction
{
public static void Main()
{
string 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";
Console.WriteLine(string.Join(",", Ranges(s.Split(',').Select(int.Parse))
.Select(r => r.end == r.start ? $"{r.start}" : $"{r.start}-{r.end}")));
}
static IEnumerable<(int start, int end)> Ranges(IEnumerable<int> numbers) {
if (numbers == null) yield break;
var e = numbers.GetEnumerator();
if (!e.MoveNext()) yield break;
int start = e.Current;
int end = start;
while (e.MoveNext()) {
if (e.Current - end != 1) {
if (end - start == 1) {
yield return (start, start);
yield return (end, end);
} else {
yield return (start, end);
}
start = e.Current;
}
end = e.Current;
}
yield return (start, end);
}
}</syntaxhighlight>
{{out}}
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
=={{header|C++}}==
 
<langsyntaxhighlight lang="cpp">
#include <iostream>
#include <iterator>
Line 733 ⟶ 1,641:
std::cout << std::endl;
}
</syntaxhighlight>
</lang>
{{out}}
Output:
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
=={{header|C sharpCeylon}}==
<syntaxhighlight lang="ceylon">shared void run() {
Made a fix to this very idiosyncratic code.
<lang csharp>using System;
value numbers = [
using System.Collections.Generic;
0, 1, 2, 4, 6, 7, 8, 11, 12, 14,
using System.Linq;
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
37, 38, 39
];
function asRangeFormattedString<Value>([Value*] values)
given Value satisfies Enumerable<Value> {
value builder = StringBuilder();
void append(Range<Value> range) {
if(!builder.empty) {
builder.append(",");
}
if(1 <= range.size < 3) {
builder.append(",".join(range));
} else {
builder.append("``range.first``-``range.last``");
}
}
if(nonempty values) {
variable value currentRange = values.first..values.first;
for(val in values.rest) {
if(currentRange.last.successor == val) {
currentRange = currentRange.first..val;
} else {
append(currentRange);
currentRange = val..val;
}
}
append(currentRange);
}
return builder.string;
}
value rangeString = asRangeFormattedString(numbers);
assert(rangeString == "0-2,4,6-8,11,12,14-25,27-33,35-39");
print(rangeString);
}</syntaxhighlight>
{{out}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|Clojure}}==
class RangeExtraction
{{libheader|org.flatland/useful}}
{
<syntaxhighlight lang="clojure">(use '[flatland.useful.seq :only (partition-between)])
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);
}
 
(defn nonconsecutive? [[x y]]
public static IEnumerable<IEnumerable<int>> GetRanges(string testString)
(not= (inc {x) y))
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;
}
 
(defn string-ranges [coll]
public static IEnumerable<string> RangesToStrings(IEnumerable<IEnumerable<int>> ranges)
(let [left (first coll)
{
foreachsize (varcount range in rangescoll)]
{(cond
(> size 2) (str left if"-" (range.Count()last == 1coll))
(= size 2) (str left "," {(last coll))
:else (str left))))
yield return range.Single().ToString();
}
else if (range.Count() == 2)
{
yield return range.Min() + "," + range.Max();
}
else
{
yield return range.Min() + "-" + range.Max();
}
}
}
}
</lang>
 
(defn format-with-ranges [coll]
Output:
(println (clojure.string/join ","
(map string-ranges (partition-between nonconsecutive? coll)))))</syntaxhighlight>
 
{{out}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
<pre>
=> (format-with-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])
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
=={{header|COBOL}}==
{{works with|OpenCOBOL}}
<syntaxhighlight lang="cobol"> IDENTIFICATION DIVISION.
PROGRAM-ID. extract-range-task.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 data-str PIC X(200) VALUE "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".
01 result PIC X(200).
PROCEDURE DIVISION.
CALL "extract-range" USING CONTENT data-str, REFERENCE result
DISPLAY FUNCTION TRIM(result)
GOBACK
.
END PROGRAM extract-range-task.
IDENTIFICATION DIVISION.
PROGRAM-ID. extract-range.
DATA DIVISION.
LOCAL-STORAGE SECTION.
COPY "nums-table.cpy".
01 difference PIC 999.
01 rng-begin PIC S999.
01 rng-end PIC S999.
01 num-trailing PIC 999.
01 trailing-comma-pos PIC 999.
 
LINKAGE SECTION.
01 nums-str PIC X(200).
01 extracted-range PIC X(200).
01 extracted-range-len CONSTANT LENGTH extracted-range.
 
PROCEDURE DIVISION USING nums-str, extracted-range.
CALL "split-nums" USING CONTENT nums-str, ", ",
REFERENCE nums-table
*> Process the table
MOVE nums (1) TO rng-begin
PERFORM VARYING nums-idx FROM 2 BY 1
UNTIL num-nums < nums-idx
SUBTRACT nums (nums-idx - 1) FROM nums (nums-idx)
GIVING difference
*> If number is more than one away from the previous one
*> end the range and start a new one.
IF difference > 1
MOVE nums (nums-idx - 1) TO rng-end
CALL "add-next-range" USING CONTENT rng-begin,
rng-end, REFERENCE extracted-range
MOVE nums (nums-idx) TO rng-begin
END-IF
END-PERFORM
*> Process the last number
MOVE nums (num-nums) TO rng-end
CALL "add-next-range" USING CONTENT rng-begin,
rng-end, REFERENCE extracted-range
*> Remove trailing comma.
CALL "find-num-trailing-spaces"
USING CONTENT extracted-range, REFERENCE num-trailing
COMPUTE trailing-comma-pos =
extracted-range-len - num-trailing
MOVE SPACE TO extracted-range (trailing-comma-pos:1)
GOBACK
.
IDENTIFICATION DIVISION.
PROGRAM-ID. split-nums INITIAL.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 num-len PIC 9.
01 next-num-pos PIC 999.
LINKAGE SECTION.
01 str PIC X(200).
01 delim PIC X ANY LENGTH.
COPY "nums-table.cpy".
PROCEDURE DIVISION USING str, delim, nums-table.
INITIALIZE num-nums
PERFORM UNTIL str = SPACES
INITIALIZE num-len
INSPECT str TALLYING num-len FOR CHARACTERS BEFORE delim
ADD 1 TO num-nums
*> If there are no more instances of delim in the string,
*> add the rest of the string to the last element of the
*> table.
IF num-len = 0
MOVE str TO nums (num-nums)
EXIT PERFORM
ELSE
MOVE str (1:num-len) TO nums (num-nums)
ADD 3 TO num-len GIVING next-num-pos
MOVE str (next-num-pos:) TO str
END-IF
END-PERFORM
.
END PROGRAM split-nums.
IDENTIFICATION DIVISION.
PROGRAM-ID. add-next-range INITIAL.
 
DATA DIVISION.
WORKING-STORAGE SECTION.
01 num-trailing PIC 999.
01 start-pos PIC 999.
01 range-len PIC 999.
01 begin-edited PIC -ZZ9.
01 end-edited PIC -ZZ9.
 
LINKAGE SECTION.
01 rng-begin PIC S999.
01 rng-end PIC S999.
01 extracted-range PIC X(200).
 
01 extracted-range-len CONSTANT LENGTH extracted-range.
PROCEDURE DIVISION USING rng-begin, rng-end, extracted-range.
CALL "find-num-trailing-spaces"
USING CONTENT extracted-range, REFERENCE num-trailing
COMPUTE start-pos = extracted-range-len - num-trailing + 1
SUBTRACT rng-begin FROM rng-end GIVING range-len
MOVE rng-begin TO begin-edited
MOVE rng-end TO end-edited
 
EVALUATE TRUE
WHEN rng-begin = rng-end
STRING FUNCTION TRIM(begin-edited), ","
INTO extracted-range (start-pos:)
WHEN range-len = 1
STRING FUNCTION TRIM(begin-edited), ",",
FUNCTION TRIM(end-edited), ","
INTO extracted-range (start-pos:)
WHEN OTHER
STRING FUNCTION TRIM(begin-edited), "-",
FUNCTION TRIM(end-edited), ","
INTO extracted-range (start-pos:)
END-EVALUATE
.
END PROGRAM add-next-range.
IDENTIFICATION DIVISION.
PROGRAM-ID. find-num-trailing-spaces.
DATA DIVISION.
LINKAGE SECTION.
01 str PIC X(200).
01 num-trailing PIC 999.
PROCEDURE DIVISION USING str, num-trailing.
INITIALIZE num-trailing
INSPECT str TALLYING num-trailing FOR TRAILING SPACES
.
END PROGRAM find-num-trailing-spaces.
END PROGRAM extract-range.</syntaxhighlight>
 
nums-table.cpy:
<syntaxhighlight lang="cobol"> 01 nums-table.
03 num-nums PIC 999.
03 nums-area.
05 nums PIC S999 OCCURS 1 TO 100 TIMES
DEPENDING ON num-nums
INDEXED BY nums-idx.</syntaxhighlight>
 
{{out}}
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
=={{header|Common Lisp}}==
<langsyntaxhighlight lang="lisp">(defun format-with-ranges (list)
(unless list (return ""))
(with-output-to-string (s)
Line 830 ⟶ 1,943:
37 38 39))
"0-2,4,6-8,11,12,14-25,27-33,35-39"
</syntaxhighlight>
</lang>
 
=={{header|D}}==
<langsyntaxhighlight lang="d">import std.stdio, std.conv, std.string, std.algorithm, std.range;
 
string extractRangesrangeExtraction(in int[] items)
in {
assert(items.isSorted());
} body {
if (items.empty)
string[] ranges;
return null;
auto ranges = [[items[0].text]];
 
forforeach (size_timmutable ix, =immutable 0y; i < items.length;zip(items[1 i++).. {$]))
immutableif low(x + 1 == items[i];y)
while (i < (items.length ranges[$ - 1) && (items[i] + 1) =~= items[i + 1])y.text;
i++;
immutable hi = items[i];
 
if (hi - low >= 2)
ranges ~= text(low, '-', hi);
else if (hi - low == 1)
ranges ~= text(low, ',', hi);
else
ranges ~= [y.text(low)];
}
 
return ranges.join(",");
.map!(r => r.length > 2 ? r[0] ~ "-" ~ r.back : r.join(','))
.join(',');
}
 
Line 865 ⟶ 1,974:
19, 20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31,
32, 33, 35, 36, 37, 38, 39]])
writeln(extractRanges(data)).rangeExtraction.writeln;
}</langsyntaxhighlight>
{{out}}
<pre>-8--6,-3-1,3-5,7-11,14,15,17-20
0,0,0,1,1
0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|Delphi}}==
See [https://rosettacode.org/wiki/Range_extraction#Pascal Pascal].
 
=={{header|DWScript}}==
<langsyntaxhighlight lang="delphi">procedure ExtractRanges(const values : array of Integer);
begin
var i:=0;
Line 897 ⟶ 2,009:
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
37, 38, 39]);</langsyntaxhighlight>
{{out}}
Output:
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|Dyalect}}==
 
{{trans|Go}}
 
<syntaxhighlight lang="dyalect">func rangeFormat(a) {
if a.Length() == 0 {
return ""
}
var parts = []
var n1 = 0
while true {
var n2 = n1 + 1
while n2 < a.Length() && a[n2] == a[n2-1]+1 {
n2 += 1
}
var s = a[n1].ToString()
if n2 == n1+2 {
s += "," + a[n2-1]
} else if n2 > n1+2 {
s += "-" + a[n2-1]
}
parts.Add(s)
if n2 == a.Length() {
break
}
if a[n2] == a[n2-1] {
throw "Sequence repeats value \(a[n2])"
}
if a[n2] < a[n2-1] {
throw "Sequence not ordered: \(a[n2]) < \(a[n2-1])"
}
n1 = n2
}
return String.Join(values: parts)
}
var rf = rangeFormat([
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("range format: \(rf)")</syntaxhighlight>
 
{{out}}
 
<pre>range format: 0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|E}}==
Cheeky solution: relying on the standard library for finding ranges, and just formatting them ourselves.
and just formatting them ourselves.
 
<langsyntaxhighlight lang="e">def rex(numbers :List[int]) {
var region := 0..!0
for n in numbers { region |= n..n }
Line 920 ⟶ 2,081:
}
return ",".rjoin(ranges)
}</langsyntaxhighlight>
 
<langsyntaxhighlight lang="e">? rex([
> 0, 1, 2, 4, 6, 7, 8, 11, 12, 14,
> 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
Line 928 ⟶ 2,089:
> 37, 38, 39])
# value: "0-2,4,6-8,11,12,14-25,27-33,35-39"
</syntaxhighlight>
</lang>
 
=={{header|EasyLang}}==
{{trans|Java}}
<syntaxhighlight>
func$ mkrange arr[] .
idx = 1
idx2 = 1
while idx <= len arr[]
repeat
idx2 += 1
until idx2 > len arr[] or arr[idx2] - arr[idx2 - 1] <> 1
.
if idx2 - idx > 2
r$ &= arr[idx] & "-" & arr[idx2 - 1] & ","
idx = idx2
else
while idx < idx2
r$ &= arr[idx] & ","
idx += 1
.
.
.
return substr r$ 1 (len r$ - 1)
.
print mkrange [ 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 ]
</syntaxhighlight>
{{out}}
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
=={{header|EchoLisp}}==
<syntaxhighlight lang="scheme">
(define task '(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- GROUPING
(define (group-range item acc)
(if
(or (empty? acc) (!= (caar acc) (1- item)))
(cons (cons item item) acc)
(begin (set-car! (car acc) item) acc)))
;; intermediate result
;; (foldl group-range () task)
;; → ((39 . 35) (33 . 27) (25 . 14) (12 . 11) (8 . 6) (4 . 4) (2 . 0))
;; 2- FORMATTING
(define (range->string range)
(let ((from (rest range)) (to (first range)))
(cond
((= from to) (format "%d " from))
((= to (1+ from)) (format "%d, %d " from to))
(else (format "%d-%d " from to)))))
;; 3 - FINAL
(string-join (map range->string (reverse (foldl group-range () task))) ",")
→ "0-2 ,4 ,6-8 ,11, 12 ,14-25 ,27-33 ,35-39 "
</syntaxhighlight>
 
=={{header|Eiffel}}==
<syntaxhighlight lang="eiffel">
class
RANGE
 
create
make
 
feature
make
local
extended_range: STRING
do
extended_range := "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("Extended range: " + extended_range + "%N")
print("Extracted range: " + extracted_range(extended_range) + "%N%N")
end
 
feature
extracted_range(sequence: STRING): STRING
local
elements: LIST[STRING]
first, curr: STRING
subrange_size, index: INTEGER
do
sequence.replace_substring_all (", ", ",")
elements := sequence.split (',')
from
index := 2
first := elements.at (1)
subrange_size := 0
Result := ""
until
index > elements.count
loop
curr := elements.at (index)
if curr.to_integer - first.to_integer - subrange_size = 1
then
subrange_size := subrange_size + 1
else
Result.append(first)
if (subrange_size <= 1)
then
Result.append (", ")
else
Result.append (" - ")
end
if (subrange_size >= 1)
then
Result.append ((first.to_integer + subrange_size).out)
Result.append (", ")
end
 
first := curr
subrange_size := 0
end
index := index + 1
end
Result.append(first)
if (subrange_size <= 1)
then
Result.append (", ")
else
Result.append (" - ")
end
if (subrange_size >= 1)
then
Result.append ((first.to_integer + subrange_size).out)
end
end
end
</syntaxhighlight>
{{Out}}
<pre>
Extended range: 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 range: 0 - 2, 4, 6 - 8, 11, 12, 14 - 25, 27 - 33, 35 - 39
</pre>
 
=={{header|Elixir}}==
{{trans|Ruby}}
<syntaxhighlight lang="elixir">defmodule RC do
def range_extract(list) do
max = Enum.max(list) + 2
sorted = Enum.sort([max|list])
candidate_number = hd(sorted)
current_number = hd(sorted)
extract(tl(sorted), candidate_number, current_number, [])
end
defp extract([], _, _, range), do: Enum.reverse(range) |> Enum.join(",")
defp extract([next|rest], candidate, current, range) when current+1 >= next do
extract(rest, candidate, next, range)
end
defp extract([next|rest], candidate, current, range) when candidate == current do
extract(rest, next, next, [to_string(current)|range])
end
defp extract([next|rest], candidate, current, range) do
separator = if candidate+1 == current, do: ",", else: "-"
str = "#{candidate}#{separator}#{current}"
extract(rest, next, next, [str|range])
end
end
 
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
]
IO.inspect RC.range_extract(list)</syntaxhighlight>
 
{{out}}
<pre>
"0-2,4,6-8,11,12,14-25,27-33,35-39"
</pre>
 
=={{header|Emacs Lisp}}==
 
{{libheader|Gnus}}
<syntaxhighlight lang="lisp">(require 'gnus-range)
 
(defun rangext (lst)
(mapconcat (lambda (item)
(if (consp item)
(if (= (+ 1 (car item)) (cdr item))
(format "%d,%d" (car item) (cdr item))
(format "%d-%d" (car item) (cdr item)))
(format "%d" item)))
(gnus-compress-sequence lst)
","))
 
(rangext '(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"</syntaxhighlight>
 
Vanilla:
<syntaxhighlight lang="lisp">(defun split-into-ranges (numbers)
(let* ((last-number (pop numbers))
(range (list last-number))
ranges)
(dolist (n numbers)
(if (= n (1+ last-number))
(push n range)
(push (nreverse range) ranges)
(setq range (list n)))
(setq last-number n))
(nreverse (cons (nreverse range) ranges))))
 
(defun format-range (range)
(cond
((not range)
(error "invalid range"))
((= (length range) 1)
(number-to-string (car range)))
((= (length range) 2)
(format "%d,%d" (car range) (cadr range)))
(t
(format "%d-%d" (car range) (car (last range))))))
 
(defun rangext (numbers)
(mapconcat #'format-range (split-into-ranges numbers) ","))
 
(rangext '(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"</syntaxhighlight>
 
=={{header|Erlang}}==
<syntaxhighlight lang="erlang">
-module( range ).
 
-export( [extraction/1, task/0] ).
 
extraction( [H | T] ) when is_integer(H) ->
Reversed_extracts = extraction_acc( lists:foldl(fun extraction/2, {H, []}, T) ),
string:join( lists:reverse(Reversed_extracts), "," ).
 
task() ->
io:fwrite( "~p~n", [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])] ).
 
 
 
extraction( N, {Start, Acc} ) when N =:= Start + 1 -> {Start, N, Acc};
extraction( N, {Start, Acc} ) -> {N, extraction_acc( {Start, Acc} )};
extraction( N, {Start, Stop, Acc} ) when N =:= Stop + 1 -> {Start, N, Acc};
extraction( N, {Start, Stop, Acc} ) -> {N, extraction_acc( {Start, Stop, Acc} )}.
 
extraction_acc( {N, Acc} ) -> [erlang:integer_to_list(N) | Acc];
extraction_acc( {Start, Stop, Acc} ) when Stop > Start + 1 -> [erlang:integer_to_list(Start) ++ "-" ++ erlang:integer_to_list(Stop) | Acc];
extraction_acc( {Start, Stop, Acc} ) -> [erlang:integer_to_list(Stop), erlang:integer_to_list(Start) | Acc]. % Reversed
</syntaxhighlight>
{{out}}
<pre>
19> range:task().
"0-2,4,6-8,11,12,14-25,27-33,35-39"
</pre>
 
=={{header|Euphoria}}==
<langsyntaxhighlight lang="euphoria">function extract_ranges(sequence s)
integer first
sequence out
Line 962 ⟶ 2,384:
 
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}))</langsyntaxhighlight>
 
{{out}}
Output:
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|F_Sharp|F#}}==
<langsyntaxhighlight lang="fsharp">let extractRanges = function
| [] -> Seq.empty
| x::xr ->
Line 991 ⟶ 2,413:
 
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 ])</langsyntaxhighlight>
 
{{out}}
Output:
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|Factor}}==
The <code>monotonic-split</code> word enables us to split the input sequence into sub-sequences of contiguous integers. From there, we make ranges out of sequences greater than 2 in length and list members of sequences less than or equal to 2 in length.
<syntaxhighlight lang="factor">USING: formatting io kernel math math.parser sequences
splitting.monotonic ;
IN: rosetta-code.range-extraction
 
: make-range ( seq -- str )
[ first ] [ last ] bi "%d-%d" sprintf ;
: make-atomic ( seq -- str ) [ number>string ] map "," join ;
 
: extract-range ( seq -- str )
[ - -1 = ] monotonic-split
[ dup length 2 > [ make-range ] [ make-atomic ] if ] map
"," join ;
{
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-range print</syntaxhighlight>
{{out}}
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
=={{header|Forth}}==
<langsyntaxhighlight lang="forth">create values
here
0 , 1 , 2 , 4 , 6 , 7 , 8 , 11 , 12 , 14 ,
Line 1,023 ⟶ 2,470:
;
 
values /values .ranges</langsyntaxhighlight>
 
{{out}}
Output:
<pre>0-2, 4, 6-8, 11, 12, 14-25, 27-33, 35-39</pre>
 
=={{header|Fortran}}==
There was some initial confusion as to whether the list was to be supplied as an array of integer values, or as a text string from which integer values were to be extracted. The consensus is a text string. In principle the text string could be parsed to find the starting and stopping positions of each number so that any size integers could be processed merely by copying the texts around without reading the values into integer variables of limited capacity, but that would be complicated by the possible presence of signs. So, it was simpler to take advantage of the free-format data reading protocol that would handle signs without difficulty and on output any spurious +signs would be omitted. This however immediately raises the question: how many numbers are there to be read? A very useful input style is to start with the number of values to read followed by the values; then something like <code>READ(IN,*) N,A(1:N)</code> works nicely. But this is not the given style of input, so a fallback: count how many commas appear to deduce how many numbers there are to be read. The free-format style allows either commas or spaces between numbers (and if there is a comma, any spaces also present are passed by), so the layout is easy. Data errors could still be encountered, so a more complete version would have <code>READ (TEXT,*,ERR=''label'') VAL(1:N)</code> to catch these, but the specification does not call for checking.
 
The standard problem is "how long is a piece of string?" - arrays normally must be given a specific bound. With F90, it is possible to allocate an array of a size determined at run time via some tedious gibberish, but for this example, LOTS will suffice. More seriously, the specification calls for a function returning the text representation of the list, but unfortunately, a function must have a specified size as in <code>CHARACTER*66 FUNCTION IRANGE(TEXT)</code> where the 66 is fixed at compile time. With Fortran 2003, there are facilities for the run-time sizing of character variables, but not in F90/95 though they could be devised with a great deal of blather. In any case, the size required is not known until the end, so successively reallocating space of size 1, 2, 3, 4, ... and each time copying the existing text into the larger text area would soon be painful. A largeish value for the size of the result could be used but instead, a subroutine, which returns its result via modifying its parameter. It is up to the caller to provide a parameter of sufficient size.
 
Although Pascal offers a Str procedure for converting a variable to a text string, maddeningly, it is a procedure not a function and so cannot be used within a compound statement. Fortran could offer access to the FORMAT facility via something like a function FMT(x) which returns the text representation of variable x with no leading or trailing spaces (whereby FMT(-6) would return "-6" and so forth) but alas, does not. Such a function cannot be written in ordinary Fortran until such time as it is possible to return varying-sized character results. The I0 format code standardised in F90 comes close but of course it must be used in a complex environment. All in all, it is easier to devise a subroutine SPLOT(n) to write the value of an integer (with possible leading hyphen if negative) to a scratchpad and then EMIT its text character by character to the output variable character until stopped by a space. Subroutines EMIT and SPLOT could be normal separate subroutines, but as servants of IRANGE it is easier to take advantage of the F90 facility whereby they can be "contained" inside IRANGE and thereby gain access to its internal context. Otherwise, there would have to be additional parameters or usage of COMMON variables for such communication.
 
The method grinds through the list of values, looking ahead for consecutive continuations (relying on the value of a DO-loop's index variable being available on exit from the loop) and thereby placing in its output string either a range of numbers or a single number. This could be done by using WRITE with suitable FORMAT statements to appropriate portions of the output string via careful counting of positions, but using EMIT and SPLOT avoids the requisite cogitations. A fancier method would be to devise a list of numbers to be output along with a suitable FORMAT statement that would supply the commas and hyphens as appropriate. Of course, one would again face the question "how long is a FORMAT string?", so, grinding stepwise it is. <syntaxhighlight lang="fortran"> SUBROUTINE IRANGE(TEXT) !Identifies integer ranges in a list of integers.
Could make this a function, but then a maximum text length returned would have to be specified.
CHARACTER*(*) TEXT !The list on input, the list with ranges on output.
INTEGER LOTS !Once again, how long is a piece of string?
PARAMETER (LOTS = 666) !This should do, at least for demonstrations.
INTEGER VAL(LOTS) !The integers of the list.
INTEGER N !Count of numbers.
INTEGER I,I1 !Steppers.
N = 1 !Presume there to be one number.
DO I = 1,LEN(TEXT) !Then by noticing commas,
IF (TEXT(I:I).EQ.",") N = N + 1 !Determine how many more there are.
END DO !Step alonmg the text.
IF (N.LE.2) RETURN !One comma = two values. Boring.
IF (N.GT.LOTS) STOP "Too many values!"
READ (TEXT,*) VAL(1:N) !Get the numbers, with free-format flexibility.
TEXT = "" !Scrub the parameter!
L = 0 !No text has been placed.
I1 = 1 !Start the scan.
10 IF (L.GT.0) CALL EMIT(",") !A comma if there is prior text.
CALL SPLOT(VAL(I1)) !The first number always appears.
DO I = I1 + 1,N !Now probe ahead
IF (VAL(I - 1) + 1 .NE. VAL(I)) EXIT !While values are consecutive.
END DO !Up to the end of the remaining list.
IF (I - I1 .GT. 2) THEN !More than two consecutive values seen?
CALL EMIT("-") !Yes!
CALL SPLOT(VAL(I - 1)) !The ending number of a range.
I1 = I !Finger the first beyond the run.
ELSE !But if too few to be worth a span,
I1 = I1 + 1 !Just finger the next number.
END IF !So much for that starter.
IF (I.LE.N) GO TO 10 !Any more?
CONTAINS !Some assistants to save on repetition.
SUBROUTINE EMIT(C) !Rolls forth one character.
CHARACTER*1 C !The character.
L = L + 1 !Advance the finger.
IF (L.GT.LEN(TEXT)) STOP "Ran out of text!" !Maybe not.
TEXT(L:L) = C !And place the character.
END SUBROUTINE EMIT !That was simple.
SUBROUTINE SPLOT(N) !Rolls forth a signed number.
INTEGER N !The number.
CHARACTER*12 FIELD !Sufficient for 32-bit integers.
INTEGER I !A stepper.
WRITE (FIELD,"(I0)") N!Roll the number, with trailing spaces.
DO I = 1,12 !Now transfer the text of the number.
IF (FIELD(I:I).LE." ") EXIT !Up to the first space.
CALL EMIT(FIELD(I:I)) !One by one.
END DO !On to the end.
END SUBROUTINE SPLOT !Not so difficult either.
END !So much for IRANGE.
 
PROGRAM POKE
CHARACTER*(200) SOME
SOME = " 0, 1, 2, 4, 6, 7, 8, 11, 12, 14, "
1 //" 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,"
2 //"25, 27, 28, 29, 30, 31, 32, 33, 35, 36, "
3 //"37, 38, 39 "
CALL IRANGE(SOME)
WRITE (6,*) SOME
END</syntaxhighlight>
 
Output: spaces after the commas could be added easily enough.<pre> 0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|Go}}==
<langsyntaxhighlight lang="go">package main
 
import (
Line 1,083 ⟶ 2,599:
}
return strings.Join(parts, ","), nil
}</langsyntaxhighlight>
{{out}}
Output:
<pre>
range format: 0-2,4,6-8,11,12,14-25,27-33,35-39
Line 1,091 ⟶ 2,607:
=={{header|Groovy}}==
Ad Hoc Solution:
<langsyntaxhighlight lang="groovy">def range = { s, e -> s == e ? "${s}," : s == e - 1 ? "${s},${e}," : "${s}-${e}," }
 
def compressList = { list ->
Line 1,102 ⟶ 2,618:
}
 
def compressRanges = { expanded -> compressList(Eval.me('[' + expanded + ']')) }</langsyntaxhighlight>
 
Test:
<langsyntaxhighlight lang="groovy">def s = '''
0, 1, 2, 4, 6, 7, 8, 11, 12, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
Line 1,111 ⟶ 2,627:
37, 38, 39
'''
println (compressRanges(s))</langsyntaxhighlight>
 
{{out}}
Output:
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|Haskell}}==
===direct recursion===
 
<langsyntaxhighlight lang="haskell">import Data.List (intercalate)
 
extractRange :: [Int] -> String
Line 1,130 ⟶ 2,646:
g a [] = (a - 1, [])
f (x : xs) = show x : f xs
f [] = []</langsyntaxhighlight>
 
<langsyntaxhighlight 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"</langsyntaxhighlight>
 
===splitBy===
We can, alternatively, define a reusable splitBy function, which returns a list of lists (split wherever the relationship between two consecutive items matches a supplied predicate function).
Delegating to splitBy allows a reasonably clean definition of range formatting:
 
<syntaxhighlight lang="haskell">import Data.List (intercalate)
import Data.Function (on)
 
----------------------- RANGE FORMAT ---------------------
rangeFormat :: [Int] -> String
rangeFormat = intercalate "," . fmap rangeString . splitBy ((/=) . succ)
 
rangeString xs
| 2 < length xs = x ++ '-' : last t
| otherwise = intercalate "," ps
where
ps@(x:t) = show <$> xs
 
--------------------- GENERIC FUNCTION -------------------
-- Split wherever a supplied predicate matches the
-- relationship between two consecutive items.
splitBy :: (a -> a -> Bool) -> [a] -> [[a]]
splitBy _ [] = []
splitBy _ [x] = [[x]]
splitBy f xs@(_:t) = uncurry (:) $ foldr go ([], []) (zip xs t)
where
go (x, prev) (active, acc)
| f x prev = ([x], current : acc)
| otherwise = (x : current, acc)
where
current
| null active = [prev]
| otherwise = active
 
--------------------------- TEST -------------------------
main :: IO ()
main =
print $
rangeFormat
[ 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
]</syntaxhighlight>
{{Out}}
<pre>"0-2,4,6-8,11,12,14-25,27-33,35-39"</pre>
 
===chop===
Or, we can pass a span-chopping function to Data.List.Split '''chop'''.
 
<syntaxhighlight lang="haskell">import Data.List (intercalate, groupBy, isPrefixOf)
import Data.List.Split (chop)
import Data.Bool (bool)
 
rangeFormat :: [Int] -> String
rangeFormat xs =
intercalate "," $
(head . ((bool <*> tail) <*> (> 1) . length)) <$>
groupBy isPrefixOf (rangeString <$> chop succSpan (zip xs (tail xs)))
rangeString [] = ""
rangeString xxs@(x:xs)
| null xs = show (snd x)
| otherwise = intercalate "-" (show <$> [fst x, snd (last xs)])
 
succSpan [] = ([], [])
succSpan (xxs@(x:xs))
| null ys = ([x], xs)
| otherwise = (ys, zs)
where
(ys, zs) = span (uncurry ((==) . succ)) xxs
 
main :: IO ()
main =
putStrLn $
rangeFormat [ 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 ]</syntaxhighlight>
{{Out}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|Icon}} and {{header|Unicon}}==
<langsyntaxhighlight Iconlang="icon">procedure main()
 
R := [ 0, 1, 2, 4, 6, 7, 8, 11, 12, 14,
Line 1,168 ⟶ 2,794:
every (s := "[ ") ||:= !L || " "
return s || "]"
end</langsyntaxhighlight>
{{out}}
Sample output:
<pre>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 ]
Line 1,176 ⟶ 2,802:
=={{header|J}}==
 
<langOlder jversions of J will also need <code> require 'strings'</code>.
 
fmt=: [: ;@(8!:0) [`]`({. ; (',-' {~ 2 < #) ; {:)@.(2 <. #)
<syntaxhighlight lang="j">fmt=: [: ;@(8!:0) [`]`({. ; (',-' {~ 2 < #) ; {:)@.(2 <. #)
group=: <@fmt;.1~ 1 ~: 0 , 2 -~/\ ]
extractRange=: ',' joinstring group</langsyntaxhighlight>
 
Example use:
 
<langsyntaxhighlight 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</langsyntaxhighlight>
 
and
 
<syntaxhighlight lang="j"> extractRange (-6, 3, 2, 1), 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20
-6,-3-1,3-5,7-11,14,15,17-20</syntaxhighlight>
 
Other examples:
 
<langsyntaxhighlight Jlang="j"> extractRange i.101
0-100</langsyntaxhighlight>
 
The first 101 non-negative integers
 
<syntaxhighlight lang="j">
<lang J>
extractRange (-. p:) i.101
0,1,4,6,8-10,12,14-16,18,20-22,24-28,30,32-36,38-40,42,44-46,48-52,54-58,60,62-66,68-70,72,74-78,80-82,84-88,90-96,98-100</langsyntaxhighlight>
 
Excluding those which are prime
 
<syntaxhighlight lang="j">
<lang J>
extractRange 2}. (-. p:) i.101
4,6,8-10,12,14-16,18,20-22,24-28,30,32-36,38-40,42,44-46,48-52,54-58,60,62-66,68-70,72,74-78,80-82,84-88,90-96,98-100</langsyntaxhighlight>
 
Also excluding the first two non-negative integers (which are neither prime nor the product of non-empty lists of primes).
 
=={{header|Java}}==
<syntaxhighlight lang="java">public class RangeExtraction {
<!--this probably isn't the best way, but it works. I may change it later. -->
 
<lang java>public class Range{
public static void main(String[] args) {
int[] arr = {0, 1, 2, 4, 6, 7, 8, 11, 12, 14,
System.out.println(compress2Range("-6, -3, -2, -1, 0, 1, 3, 4, 5, 7," +
" 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20"));, 21, 22, 23, 24,
25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
System.out.println(compress2Range(
"037, 138, 2, 4, 6, 7, 8, 11, 12, 14, " +39};
 
"15, 16, 17, 18, 19, 20, 21, 22, 23, 24," +
int len = arr.length;
"25, 27, 28, 29, 30, 31, 32, 33, 35, 36," +
int idx = 0, idx2 = "37, 38, 39"))0;
while (idx < len) {
}
while (++idx2 < len && arr[idx2] - arr[idx2 - 1] == 1);
if (idx2 - idx > 2) {
private static String compress2Range(String expanded){
System.out.printf("%s-%s,", arr[idx], arr[idx2 - 1]);
StringBuilder result = new StringBuilder();
String[] nums = expanded.replace(" ", "").split(",") idx = idx2;
int firstNum = Integer.parseInt(nums[0]); } else {
int rangeSize = 0 for (; idx < idx2; idx++)
for(int i = 1; i < nums System.length;out.printf("%s,", i++arr[idx]){;
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();
}
}</langsyntaxhighlight>
{{out}}
Output:
<pre>0-62,-3-14,36-58,7-11,12,14-25,1527-33,1735-2039,</pre>
0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|JavaScript}}==
===ES5===
<lang javascript>function rangeExtraction(list) {
====Imperative====
<syntaxhighlight lang="javascript">function rangeExtraction(list) {
var len = list.length;
var out = [];
Line 1,287 ⟶ 2,900:
25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
37, 38, 39
]));</langsyntaxhighlight>
 
====Functional====
{{Trans|ES6}}
{{Trans|Haskell}}
<syntaxhighlight lang="javascript">(function () {
'use strict';
 
// rangeFormat :: [Int] -> String
var rangeFormat = function (xs) {
return splitBy(function (a, b) {
return b - a > 1;
}, xs)
.map(rangeString)
.join(',');
};
 
// rangeString :: [Int] -> String
var rangeString = function (xs) {
return xs.length > 2 ? [head(xs), last(xs)].map(show)
.join('-') : xs.join(',');
};
 
// GENERIC FUNCTIONS
 
// Splitting not on a delimiter, but whenever the relationship between
// two consecutive items matches a supplied predicate function
 
// splitBy :: (a -> a -> Bool) -> [a] -> [[a]]
var splitBy = function (f, xs) {
if (xs.length < 2) return [xs];
var h = head(xs),
lstParts = xs.slice(1)
.reduce(function (a, x) {
var acc = a[0],
active = a[1],
prev = a[2];
 
return f(prev, x) ? (
[acc.concat([active]), [x], x]
) : [acc, active.concat(x), x];
}, [
[],
[h], h
]);
return lstParts[0].concat([lstParts[1]]);
};
 
// head :: [a] -> a
var head = function (xs) {
return xs.length ? xs[0] : undefined;
};
 
// last :: [a] -> a
var last = function (xs) {
return xs.length ? xs.slice(-1)[0] : undefined;
};
 
// show :: a -> String
var show = function (x) {
return JSON.stringify(x);
};
 
// TEST
return rangeFormat([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
]);
})();</syntaxhighlight>
 
{{Out}}
<pre>"0-2,4,6-8,11,12,14-25,27-33,35-39"</pre>
 
===ES6===
====Composition of pure functions====
{{Trans|Haskell}}
Defining the range format in terms of a reusable '''splitBy''' function:
<syntaxhighlight lang="javascript">(() => {
'use strict';
 
// ---------------- RANGE EXTRACTION -----------------
 
// rangeFormat :: [Int] -> String
const rangeFormat = xs =>
splitBy((a, b) => b - a > 1, xs)
.map(rangeString)
.join(',');
 
// rangeString :: [Int] -> String
const rangeString = xs =>
xs.length > 2 ? (
[xs[0], last(xs)].map(show)
.join('-')
) : xs.join(',')
 
 
// ---------------------- TEST -----------------------
const main = () =>
rangeFormat([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
]);
 
 
// ---------------- GENERIC FUNCTIONS ----------------
 
// Splitting not on a delimiter, but whenever the
// relationship between two consecutive items matches
// a supplied predicate function
 
// splitBy :: (a -> a -> Bool) -> [a] -> [[a]]
const splitBy = (f, xs) => {
if (xs.length < 2) return [xs];
const
h = xs[0],
lstParts = xs.slice(1)
.reduce(([acc, active, prev], x) =>
f(prev, x) ? (
[acc.concat([active]), [x], x]
) : [acc, active.concat(x), x], [
[],
[h],
h
]);
return lstParts[0].concat([lstParts[1]]);
};
 
// last :: [a] -> a
const last = xs => (
// The last item of a list.
ys => 0 < ys.length ? (
ys.slice(-1)[0]
) : undefined
)(xs);
 
// show :: a -> String
const show = x =>
JSON.stringify(x);
 
// MAIN --
return main();
})();</syntaxhighlight>
{{Out}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
====Idiomatic====
<syntaxhighlight lang="javascript">
function toRange(arr) {
const ranges = [];
const sorted = [...arr.filter(Number.isInteger)].sort((x,y) => Math.sign(x-y));
const sequenceBreak = (x,y) => y - x > 1 ;
 
let i = 0;
while ( i < sorted.length ) {
 
let j = i ;
while ( j < sorted.length - 1 && !sequenceBreak( sorted[j], sorted[j+1] ) ) {
++j;
}
 
const from = sorted[i];
const thru = sorted[j];
const rangeLen = 1 + j - i;
 
if ( from === thru ) {
ranges.push( [from] );
} else {
if ( rangeLen > 2 ) {
ranges.push([from,thru]);
} else {
ranges.push([from], [thru]);
}
}
 
i = j+1;
}
 
return ranges.map( range => range.join('-') ).join(',');
}
 
// -----------------------------------------------------------------------------
// Test Case
// -----------------------------------------------------------------------------
const expected = '0-2,4,6-8,11,12,14-25,27-33,35-39';
const actual = toRange([
0, 1, 2,
4,
6, 7, 8,
11, 12, // should be two singletons
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
27, 28, 29, 30, 31, 32, 33,
35, 36, 37, 38, 39,
]);
 
console.log(`actual output : '${actual}'.`);
console.log(`expected output : '${expected}'.`);
console.log(`Correct? ${ actual === expected ? 'Yes' : 'No'}`);
</syntaxhighlight>
{{Out}}
<pre>
actual output : '0-2,4,6-8,11,12,14-25,27-33,35-39'.
expected output : '0-2,4,6-8,11,12,14-25,27-33,35-39'.
Correct? Yes</pre>
 
=={{header|jq}}==
<syntaxhighlight lang="kq"># Input should be an array
def extract:
reduce .[] as $i
# state is an array with integers or [start, end] ranges
([];
if length == 0 then [ $i ]
else ( .[-1]) as $last
| if ($last|type) == "array" then
if ($last[1] + 1) == $i then setpath([-1,1]; $i)
else . + [ $i ]
end
elif ($last + 1) == $i then setpath([-1]; [$last, $i])
else . + [ $i ]
end
end)
| map( if type == "number" then tostring
elif .[0] == .[1] -1
then "\(.[0]),\(.[1])" # satisfy special requirement
else "\(.[0])-\(.[1])" end )
| join(",") ;</syntaxhighlight>
 
{{out|Command and output}}
<pre>
$ jq -n -f extract_range.jq input.txt
"0-2,4,6-8,11,12,14-25,27-33,35-39"</pre>
 
=={{header|Jsish}}==
From Javascript ES5 Imperative solution.
<syntaxhighlight lang="javascript">/* Range Extraction, in Jsish */
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 (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("");
}
 
var arr = [ 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 ];
 
puts(arr);
puts(rangeExtraction(arr));</syntaxhighlight>
 
{{out}}
<pre>prompt$ jsish rangeExtraction.jsi
[ 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</pre>
 
=={{header|Julia}}==
This is perhaps an idiosyncratic solution. Numbers inside of runs are replaced with Xs, the list is converted into a comma separated string, and then Xs and extra commas are replaced with the range character via a regular expression.
<syntaxhighlight lang="julia">
function sprintfrange{T<:Integer}(a::Array{T,1})
len = length(a)
0 < len || return ""
dropme = falses(len)
dropme[2:end-1] = Bool[a[i-1]==a[i]-1 && a[i+1]==a[i]+1 for i in 2:(len-1)]
s = [string(i) for i in a]
s[dropme] = "X"
s = join(s, ",")
replace(s, r",[,X]+,", "-")
end
testa = [ 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("Testing range-style formatting.")
println(" ", testa, "\n =>\n ", sprintfrange(testa))
</syntaxhighlight>
 
{{out}}
<pre>
[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
</pre>
 
=={{header|K}}==
<langsyntaxhighlight lang="k">grp : {(&~1=0,-':x)_ x}
fmt : {:[1=#s:$x;s;(*s),:[3>#s;",";"-"],*|s]}
erng: {{x,",",y}/,//'fmt'grp x}</langsyntaxhighlight>
 
{{out|Example:}}
<lang kpre> 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"</langpre>
 
=={{header|Liberty BASICKotlin}}==
<syntaxhighlight lang="scala">// version 1.0.6
<lang lb>
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"
print ExtractRange$( s$)
end
 
fun extractRange(list: List<Int>): String {
function ExtractRange$( range$)
if (list.isEmpty()) return ""
n = 1
countval sb = ItemCountStringBuilder( range$, ",")
whilevar nfirst <= countlist[0]
var prev = first
startValue = val( word$( range$, n, ","))
 
m = n + 1
fun append(index: Int) {
while m <= count
if nextValue(first == val(prev) word$sb.append( range$, m, ",")prev)
else if nextValue - startValue(first <>== mprev - n1) thensb.append(first, exit",", whileprev)
else sb.append(first, "-", m = m + 1prev)
if (index < list.size - 1) sb.append(",")
wend
}
if m - n > 2 then
 
ExtractRange$ = ExtractRange$ + str$( startValue) + "-" + str$( startValue + m - n - 1) + ","
for (i in 1 until list.size) {
if (list[i] == prev + 1) prev++
else {
append(i)
first = list[i]
prev = first
}
}
append(list.size - 1)
return sb.toString()
}
fun main(args: Array<String>) {
val list1 = listOf(-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20)
println(extractRange(list1))
println()
val list2 = listOf(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(extractRange(list2))
}</syntaxhighlight>
 
{{out}}
<pre>
-6,-3-1,3-5,7-11,14,15,17-20
 
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
=={{header|LiveCode}}==
Inefficient as it takes 2 passes
<syntaxhighlight lang="livecode">function rangeExtract nums
local prevNum, znums, rangedNums
set itemDelimiter to ", "
put the first item of nums into prevNum
repeat for each item n in nums
if n is (prevNum + 1) then
put n into prevNum
put "#" & n after znums
else
for i =put n to m -into 1prevNum
put return & n after znums
ExtractRange$ = ExtractRange$ + str$( startValue + i - n) + ","
next i
end if
end n = mrepeat
set itemDelimiter to "#"
wend
repeat for each line z in znums
ExtractRange$ = left$( ExtractRange$, len( ExtractRange$) - 1)
if z is empty then next repeat
end function
switch the number of items of z
case 1
put z & "," after rangedNums
break
case 2
put item 1 of z & "," & item -1 of z & "," after rangedNums
break
default
put item 1 of z & "-" & item -1 of z & "," after rangedNums
end switch
end repeat
return char 1 to -2 of rangedNums --strip off trailing comma
end rangeExtract
</syntaxhighlight>
Test
<syntaxhighlight lang="livecode">command testRangeExtract
local numbers
put "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" into numbers
put rangeExtract(numbers)
end testRangeExtract</syntaxhighlight>
Output: <syntaxhighlight lang="livecode">0-2,4,6-8,11,12,14-25,27-33,35-39</syntaxhighlight>
 
=={{header|Lua}}==
function ItemCount( list$, separator$)
<syntaxhighlight lang="lua">function extractRange (rList)
while word$( list$, ItemCount + 1, separator$) <> ""
local rExpr, ItemCountstartVal = ItemCount + 1""
for k, v in pairs(rList) do
wend
if rList[k + 1] == v + 1 then
end function
if not startVal then startVal = v end
</lang>
else
Sample output:- 0-2,4,6-8,11,12,14-25,27-33,35-39
if startVal then
if v == startVal + 1 then
rExpr = rExpr .. startVal .. "," .. v .. ","
else
rExpr = rExpr .. startVal .. "-" .. v .. ","
end
startVal = nil
else
rExpr = rExpr .. v .. ","
end
end
end
return rExpr:sub(1, -2)
end
 
local intList = {
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(extractRange(intList))</syntaxhighlight>
{{out}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|Maple}}==
<syntaxhighlight lang="maple">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]:
r1,r2:= lst[1],lst[1]:
for i from 2 to numelems(lst) do
if lst[i] - lst[i-1] = 1 then #consecutive
r2 := lst[i]:
else #break
printf(piecewise(r2-r1=1, "%d,%d,", r2-r1>1,"%d-%d,", "%d,"), r1, r2):
r1,r2:= lst[i],lst[i]:
fi:
od:
printf(piecewise(r2-r1=1, "%d,%d", r2-r1>1,"%d-%d", "%d"), r1, r2):</syntaxhighlight>
{{Out|Output}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|Mathematica}}/{{header|Wolfram Language}}==
<syntaxhighlight lang="mathematica">rangeExtract[data_List] := ToString[Row[Riffle[
<lang Mathematica>
rangeExtract[data_List] := ToString[Row[
Riffle[
Flatten[Split[Sort[data], #2 - #1 == 1 &] /. {a_Integer, __, b_} :> Row[{a, "-", b}]],
","]]];
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}]</syntaxhighlight>
]];
{{out}}
</lang>
<pre>"0-2,4,6-8,11,12,14-25,27-33,35-39"</pre>
 
=={{header|MATLAB}} / {{header|Octave}}==
Example:
<syntaxhighlight lang="matlab">function S=range_extraction(L)
<pre>
% Range extraction
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}]
L(end+1) = NaN;
S = int2str(L(1));
k = 1;
while (k < length(L)-1)
if (L(k)+1==L(k+1) && L(k)+2==L(k+2) )
m = 2;
while (L(k)+m==L(k+m))
m = m+1;
end
k = k+m-1;
S = [S,'-',int2str(L(k))];
else
k = k+1;
S = [S,',',int2str(L(k))];
end
end
end
 
"disp(range_extraction([0-, 1, 2, 4, 6-, 7, 8, 11, 12, 14-25,27-33 15,35-39" ...
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, ...
</pre>
28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39]))</syntaxhighlight>
 
{{Out|Output (Octave)}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|Mercury}}==
<syntaxhighlight lang="mercury">:- module range_extraction.
:- interface.
 
:- import_module io.
 
:- pred main(io::di, io::uo) is det.
 
:- implementation.
 
:- import_module int, list, ranges, string.
 
main(!IO) :-
print_ranges(numbers, !IO).
 
:- pred print_ranges(list(int)::in, io::di, io::uo) is det.
 
print_ranges(Nums, !IO) :-
Ranges = ranges.from_list(Nums),
ranges.range_foldr(add_range_string, Ranges, [], RangeStrs),
io.write_list(RangeStrs, ",", io.write_string, !IO).
 
:- pred add_range_string(int::in, int::in,
list(string)::in, list(string)::out) is det.
 
add_range_string(L, H, !Strs) :-
( if L = H then
!:Strs = [int_to_string(L) | !.Strs]
else if L + 1 = H then
!:Strs = [int_to_string(L), int_to_string(H) | !.Strs]
else
!:Strs = [string.format("%d-%d", [i(L), i(H)]) | !.Strs]
).
 
:- func numbers = list(int).
 
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].
</syntaxhighlight>
 
=={{header|MiniScript}}==
<syntaxhighlight lang="miniscript">extractRange = function(ints)
result = []
idx = 0
while idx < ints.len
runLen = 1
while idx+runLen < ints.len and ints[idx+runLen] == ints[idx] + runLen
runLen = runLen + 1
end while
if runLen > 2 then
result.push ints[idx] + "-" + ints[idx+runLen-1]
idx = idx + runLen
else
result.push ints[idx]
idx = idx + 1
end if
end while
return join(result, ",")
end function
 
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]
print extractRange(test)</syntaxhighlight>
 
{{out}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|MUMPS}}==
<langsyntaxhighlight MUMPSlang="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
Line 1,369 ⟶ 3,475:
IF CONT SET Y=Y_PREV
K I,CONT,NOTFIRST,CURR,PREV,NEXT,SEQ
QUIT Y</langsyntaxhighlight>
Example:
<pre>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"
Line 1,375 ⟶ 3,481:
USER>W $$RANGCONT^ROSETTA(S)
0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|NetRexx}}==
===NetRexx Ver. 1===
<syntaxhighlight lang="netrexx">/*NetRexx program to test range extraction. ***************************
* 07.08.2012 Walter Pachl derived from my Rexx Version
* Changes: line continuation in aaa assignment changed
* 1e99 -> 999999999
* Do -> Loop
* words(aaa) -> aaa.words()
* word(aaa,i) -> aaa.word(i)
**********************************************************************/
Say 'NetRexx program derived from Rexx'
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'
aaa=aaa' 30 31 32 33 35 36 37 38 39'
say 'old='aaa;
aaa=aaa 999999999 /* artificial number at the end */
i=0 /* initialize index */
ol='' /* initialize output string */
comma='' /* will become a ',' lateron */
inrange=0
Loop While i<=aaa.words /* loop for all numbers */
i=i+1 /* index of next number */
n=aaa.word(i) /* the now current number */
If n=999999999 Then Leave /* we are at the end */
If inrange Then Do /* range was opened */
If aaa.word(i+1)<>n+1 Then Do /* following word not in range */
ol=ol||n /* so this number is the end */
inrange=0 /* and the range is over */
End /* else ignore current number */
End
Else Do /* not in a range */
ol=ol||comma||n /* add number (with comma) */
comma=',' /* to the output string */
If aaa.word(i+2)=n+2 Then Do /* if the nr after the next fits */
inrange=1 /* open a range */
ol=ol'-' /* append the range connector */
End
End
End
Say 'new='ol</syntaxhighlight>
{{out}}
<pre>
NetRexx program derived from Rexx
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
</pre>
 
===NetRexx Ver. 2===
{{trans|Java}}
<syntaxhighlight lang="netrexx">/* NetRexx */
options replace format comments java crossref symbols nobinary
 
runSample(arg)
return
 
-- ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
-- Compact a list of numbers by reducing ranges
method compact(expanded) public static
nums = expanded.changestr(',', ' ').space -- remove possible commas & clean up the string
rezult = ''
 
RANGE = 0
FIRST = nums.word(1) -- set starting value
loop i_ = 2 to nums.words -- each word in the string is a number to examine
LOCAL = nums.word(i_)
if LOCAL - FIRST - RANGE == 1 then do
-- inside a range
RANGE = RANGE + 1
end
else do
-- not inside a range
if RANGE \= 0 then do
-- we have a range of numbers so collect this and reset
rezult = rezult || FIRST || delim(RANGE) || FIRST + RANGE || ','
RANGE = 0
end
else do
-- just collect this number
rezult = rezult || FIRST || ','
end
FIRST = LOCAL -- bump new starting value
end
end i_
 
if RANGE \= 0 then do
-- terminating value is a range
rezult = rezult || FIRST || delim(RANGE) || FIRST + RANGE
end
else do
-- terminating value is a single number
rezult = rezult || FIRST
end
 
return rezult.space(1, ',') -- format and return result string
 
-- ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
-- determine if the range delimiter should be a comma or dash
method delim(range) private static
if range == 1 then dlm = ','
else dlm = '-'
return dlm
 
-- ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
-- sample driver
method runSample(arg) public static
 
parse arg userInput
td = 0
if userInput.words > 0 then do
-- use input from command line
td[0] = td[0] + 1; r_ = td[0]; td[r_] = userInput
end
else do
-- use canned test data
td[0] = td[0] + 1; r_ = td[0]; td[r_] = ' -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20'
td[0] = td[0] + 1; r_ = td[0]; td[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'
td[0] = td[0] + 1; r_ = td[0]; td[r_] = ' -4, -3, -2, 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
 
loop r_ = 1 to td[0]
say 'Original: ' td[r_].changestr(',', ' ').space(1, ',')
say 'Compacted:' compact(td[r_])
say
end r_
return
</syntaxhighlight>
{{out}}
<pre>
Original: -6,-3,-2,-1,0,1,3,4,5,7,8,9,10,11,14,15,17,18,19,20
Compacted: -6,-3-1,3-5,7-11,14,15,17-20
 
Original: 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
Compacted: 0-2,4,6-8,11,12,14-25,27-33,35-39
 
Original: -4,-3,-2,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
Compacted: -4--2,0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
=={{header|Nim}}==
<syntaxhighlight lang="nim">import parseutils, re, strutils, sequtils
 
proc extractRange(input: string): string =
var list = input.replace(re"\s+").split(',').map(parseInt)
var ranges: seq[string]
var i = 0
while i < list.len:
var first = list[i] # first element in the current range
var offset = i
while True: # skip ahead to the end of the current range
if i + 1 >= list.len:
# reached end of the list
break
if list[i + 1] - (i + 1) != first - offset:
# next element isn't in the current range
break
i.inc
var last = list[i] # last element in the current range
case last - first
of 0: ranges.add($first)
of 1: ranges.add("$1,$2".format(first, last))
else: ranges.add("$1-$2".format(first, last))
i.inc
return ranges.join(",")
 
echo("""
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""".extractRange)</syntaxhighlight>
 
{{out}}
<pre>0-2, 4, 6-8, 11, 12, 14-25, 27-33, 35-39</pre>
 
=={{header|Oberon-2}}==
Oxford Oberon-2
<langsyntaxhighlight lang="oberon2">
MODULE RangeExtraction;
IMPORT Out;
Line 1,471 ⟶ 3,749:
Range(seq1)
END RangeExtraction.
</syntaxhighlight>
</lang>
{{out}}
Output:
<pre>
0- 2, 4, 6- 8, 11, 12, 14- 25, 27- 33, 35- 39
-6, -3- 1, 3- 5, 7- 11, 14, 15, 17- 20
</pre>
 
=={{header|NetRexx}}==
=={{header|Objeck}}==
<lang netrexx>/*NetRexx program to test range extraction. ***************************
{{trans|Java}}
* 07.08.2012 Walter Pachl derived from my Rexx Version
<syntaxhighlight lang="objeck">class IdentityMatrix {
* Changes: line continuation in aaa assignment changed
function : Main(args : String[]) ~ Nil {
* 1e99 -> 999999999
Compress2Range("-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20")->PrintLine();
* Do -> Loop
* words(aaa) -> aaa.words()
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")->PrintLine();
* word(aaa,i) -> aaa.word(i)
}
**********************************************************************/
Say 'NetRexx program derived from Rexx'
function : Compress2Range(expanded : String) ~ String {
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'
result := "";
aaa=aaa' 30 31 32 33 35 36 37 38 39'
nums := expanded->ReplaceAll(" ", "")->Split(",");
say 'old='aaa;
firstNum := nums[0]->ToInt();
aaa=aaa 999999999 /* artificial number at the end */
rangeSize := 0;
i=0 /* initialize index */
for(i:= 1; i < nums->Size(); i += 1;) {
ol='' /* initialize output string */
thisNum := nums[i]->ToInt();
comma='' /* will become a ',' lateron */
if(thisNum - firstNum - rangeSize = 1) {
inrange=0
Loop While i<=aaa.words /* loop for all numbers rangeSize += */1;
}
i=i+1 /* index of next number */
else{
n=aaa.word(i) /* the now current number */
if(rangeSize <> 0){
If n=999999999 Then Leave /* we are at the end */
result->Append(firstNum);
If inrange Then Do /* range was opened */
result->Append((rangeSize = 1) ? ",": "-");
If aaa.word(i+1)<>n+1 Then Do /* following word not in range */
result->Append(firstNum+rangeSize);
ol=ol||n /* so this number is the end */
result->Append(",");
inrange=0 /* and the range is over */
End rangeSize := /* else ignore current number */0;
End }
else {
Else Do /* not in a range */
result->Append(firstNum);
ol=ol||comma||n /* add number (with comma) */
result->Append(",");
comma=',' /* to the output string */
};
If aaa.word(i+2)=n+2 Then Do /* if the nr after the next fits */
firstNum := thisNum;
inrange=1 /* open a range */
};
ol=ol'-' /* append the range connector */
End};
End
if(rangeSize <> 0){
End
result->Append(firstNum);
Say 'new='ol</lang>
result->Append((rangeSize = 1) ? "," : "-");
Output
result->Append(firstNum + rangeSize);
rangeSize := 0;
}
else {
result->Append(firstNum);
};
return result;
}
}
</syntaxhighlight>
 
=={{header|Objective-C}}==
We can use <code>NSIndexSet</code> to do this.
However, it only works for non-negative integers.
{{works with|Mac OS X|10.7+}}
{{works with|iOS|5+}}
<syntaxhighlight lang="objc">#import <Foundation/Foundation.h>
 
NSString *extractRanges(NSArray *nums) {
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] init];
for (NSNumber *n in nums) {
if ([n integerValue] < 0)
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"negative number not supported" userInfo:nil];
[indexSet addIndex:[n unsignedIntegerValue]];
}
NSMutableString *s = [[NSMutableString alloc] init];
[indexSet enumerateRangesUsingBlock:^(NSRange range, BOOL *stop) {
if (s.length)
[s appendString:@","];
if (range.length == 1)
[s appendFormat:@"%lu", range.location];
else if (range.length == 2)
[s appendFormat:@"%lu,%lu", range.location, range.location+1];
else
[s appendFormat:@"%lu-%lu", range.location, range.location+range.length-1];
}];
return s;
}
 
int main() {
@autoreleasepool {
 
NSLog(@"%@", extractRanges(@[@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]));
 
}
return 0;
}</syntaxhighlight>
{{out}}
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39
NetRexx program derived from Rexx
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
</pre>
 
=={{header|OCaml}}==
<langsyntaxhighlight lang="ocaml">let range_extract = function
| [] -> []
| x::xs ->
Line 1,542 ⟶ 3,871:
in
let rng = range_extract li in
print_endline(string_of_range rng)</langsyntaxhighlight>
 
{{out}}
Output
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|OxygenBasicOl}}==
<syntaxhighlight lang="scheme">
{{output?|OxygenBasic}}
(define (extract ll)
<lang oxygenbasic>
(let loop ((head (car ll)) (tail (cdr ll)) (out #null))
dim sys ints(100)
(if (null? tail)
ints=>
0, 1, 2, 4,(reverse (cons 6,head 7, 8, 11, 12, 14,out))
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
37, 38, 39
 
function ShowRange(sys*i) as string
'==================================
pr=""
n=0
e=0
j=0
k=-1
do
j++
n=i(j)
e=i(j+1)
if e<j then
exit do
end if
if e=n+1 and i(j+2)=n+2 then 'LOOKAHEAD
if k=-1 then k=n
else
if k>=0 then
pr+=k "-" i(j+1) ", " 'RANGE OF VALUES
j++
k=-1
else
pr+=n ", " 'SINGLE VALUES(cond
((eq? head (- (car tail) 1))
end if
(loop (list (car tail) head) (cdr tail) out))
end if
((and (pair? head) (eq? (car head) (- (car tail) 1)))
end do
(loop (cons (car tail) head) (cdr tail) out))
return left pr, len(pr)-2
(else
end function
(loop (car tail) (cdr tail) (cons head out)))))))
 
(define (range->string range)
(fold (lambda (f v)
(string-append (if f (string-append f ",") "")
(if (pair? v)
(string-append (string-append (number->string (last v #f)) "-")(number->string (car v)))
(number->string v))))
#false
range))
 
; let's test
print ShowRange ints
(define 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))
</lang>
(define range (extract data))
 
(print "extracted ranges: " range)
(print "string representation: " (range->string range))
</syntaxhighlight>
{{Out}}
<pre>$ ol range_extraction.scm
extracted ranges: ((2 1 0) 4 (8 7 6) (12 11) (25 24 23 22 21 20 19 18 17 16 15 14) (33 32 31 30 29 28 27) (39 38 37 36 35))
string representation: 0-2,4,6-8,11-12,14-25,27-33,35-39
</pre>
 
=={{header|ooRexx}}==
{{trans|NetRexx Ver. 2}}
{{trans|Java}}
<syntaxhighlight lang="oorexx">/* Rexx */
 
parse arg userInput
call runSample userInput
return
exit
 
-- ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
-- Compact a list of numbers by reducing ranges
compact:
procedure
--trace ?r;nop
parse arg expanded
nums = expanded~changestr(',', ' ')~space -- remove possible commas & clean up the string
rezult = ''
 
RANGE = 0
FIRST = nums~word(1) -- set starting value
loop i_ = 2 to nums~words -- each word in the string is a number to examine
LOCAL = nums~word(i_)
if LOCAL - FIRST - RANGE == 1 then do
-- inside a range
RANGE += 1
end
else do
-- not inside a range
if RANGE \= 0 then do
-- we have a range of numbers so collect this and reset
rezult = rezult || FIRST || delim(RANGE) || FIRST + RANGE || ','
RANGE = 0
end
else do
-- just collect this number
rezult = rezult || FIRST || ','
end
FIRST = LOCAL -- bump new starting value
end
end i_
if RANGE \= 0 then do
-- collect terminating value (a range)
rezult = rezult || FIRST || delim(RANGE) || FIRST + RANGE
end
else do
-- collect terminating value (a single number)
rezult = rezult || FIRST
end
 
return rezult~space(1, ',') -- format and return result string
 
-- ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
-- determine if the range delimiter should be a comma or dash
delim:
procedure
parse arg range .
if range == 1 then dlm = ','
else dlm = '-'
return dlm
 
-- ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
-- sample driver
runSample:
procedure
parse arg userInput
td. = 0
if userInput~words > 0 then do
td.0 += 1; r_ = td.0; td.r_ = userInput
end
else do
td.0 += 1; r_ = td.0; td.r_ = '-6 -3 -2 -1 0 1 3 4 5 7 8 9 10 11 14 15 17 18 19 20'
td.0 += 1; r_ = td.0; td.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'
td.0 += 1; r_ = td.0; td.r_ = '-4, -3, -2, 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
 
loop r_ = 1 to td.0
say 'Original: ' td.r_~changestr(',', ' ')~space(1, ',')
say 'Compacted:' compact(td.r_)
say
end r_
return
</syntaxhighlight>
{{out}}
<pre>
Original: -6,-3,-2,-1,0,1,3,4,5,7,8,9,10,11,14,15,17,18,19,20
Compacted: -6,-3-1,3-5,7-11,14,15,17-20
 
Original: 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
Compacted: 0-2,4,6-8,11,12,14-25,27-33,35-39
 
Original: -4,-3,-2,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
Compacted: -4--2,0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
=={{header|Oz}}==
<langsyntaxhighlight lang="oz">declare
fun {Extract Xs}
{CommaSeparated
Line 1,637 ⟶ 4,055:
15 16 17 18 19 20 21 22 23 24
25 27 28 29 30 31 32 33 35 36
37 38 39 ]}}</langsyntaxhighlight>
 
{{out}}
Sample output:
<lang ozpre>0-2,4,6-8,11,12,14-25,27-33,35-39</langpre>
 
=={{header|PerlPascal}}==
 
{{works with|Free Pascal|2.6.2}}
{{works with|Delphi}}
 
<syntaxhighlight lang="pascal">program RangeExtractionApp;
 
 
{$IFDEF FPC}
{$mode objfpc}{$H+}
{$ENDIF}
 
 
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
SysUtils;
function RangeExtraction(const Seq: array of integer): String;
const
SubSeqLen = 3; // minimal length of the range, can be changed.
var
i, j: Integer;
Separator: string;
begin
Separator:= '';
Result := '';
i := Low(Seq);
while i <= High(Seq) do
begin
j := i;
// All subsequent values, starting from i, up to High(Seq) possibly
while ((j < High(Seq)) and ((Seq[j+1]-Seq[j]) = 1)) do
Inc(j);
// is it a range ?
if ((j-i) >= (SubSeqLen-1)) then
begin
Result := Result + Format(Separator+'%d-%d',[Seq[i],Seq[j]]);
i := j+1; // Next value to be processed
Separator := ',';
end
else
begin
// Loop, to process the case SubSeqLen > 3
while i<=j do
begin
Result := Result + Format(Separator+'%d',[Seq[i]]);
Inc(i); // Next value to be processed
Separator := ',';
end;
end;
end;
End;
procedure DisplayRange(const Seq: array of integer);
var
i: Integer;
begin
Write(Format('[%d', [Seq[Low(Seq)]]));
for i := Low(Seq) + 1 to High(Seq) do
Write(Format(',%d', [Seq[i]]));
WriteLn('] => ' + RangeExtraction(Seq));
WriteLn;
End;
begin
DisplayRange([0]);
DisplayRange([0,1]);
DisplayRange([0,2]);
DisplayRange([0,1,2]);
DisplayRange([0,1,2,3]);
DisplayRange([0,1,2,3,4,5,6,7]);
DisplayRange([0,2,3,4,5,6,7,9]);
DisplayRange([0,2,4,6,8,10]);
DisplayRange([0,1,2,3,4,5,6,7,9]);
DisplayRange([0,1,2,3,4,6,9,10,11,12]);
DisplayRange([
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]);
{$IFNDEF UNIX}readln;{$ENDIF}
end.</syntaxhighlight>
 
{{out}}
<pre>[0] => 0
 
[0,1] => 0,1
 
[0,2] => 0,2
 
[0,1,2] => 0-2
 
[0,1,2,3] => 0-3
 
[0,1,2,3,4,5,6,7] => 0-7
 
[0,2,3,4,5,6,7,9] => 0,2-7,9
 
[0,2,4,6,8,10] => 0,2,4,6,8,10
 
[0,1,2,3,4,5,6,7,9] => 0-7,9
 
[0,1,2,3,4,6,9,10,11,12] => 0-4,6,9-12
 
[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</pre>
 
=={{header|Perl}}==
Using regexes. Also handles +/- and negative integer ranges.
 
<langsyntaxhighlight Perllang="perl">sub rangext {
my $str = join ' ', @_;
1 while $str =~ s{([+-]?\d+) ([+-]?\d+)}
Line 1,656 ⟶ 4,183:
 
# 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";</langsyntaxhighlight>
 
{{out}}
Output:
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
{{libheader|Set&#58;&#58;IntSpan}}
=={{header|Perl 6}}==
<lang Perl6>sub range-extraction (*@ints) {
my $prev = NaN;
my @ranges;
 
<syntaxhighlight lang="perl">use Set::IntSpan;
for @ints -> $int {
sub rangext { return Set::IntSpan->new(@_) . '' } # stringized</syntaxhighlight>
if $int == $prev + 1 {
@ranges[*-1].push: $int;
}
else {
@ranges.push: [$int];
}
$prev = $int;
}
join ',', @ranges.map: -> @r { @r > 2 ?? "@r[0]-@r[*-1]" !! @r }
}
 
{{libheader|Set&#58;&#58;IntSpan&#58;&#58;Fast}}
say range-extraction
-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20;
 
<syntaxhighlight lang="perl">use Set::IntSpan::Fast;
say range-extraction
sub rangext { return Set::IntSpan::Fast->new(@_)->as_string }</syntaxhighlight>
0, 1, 2, 4, 6, 7, 8, 11, 12, 14,
 
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
<code>Set::IntSpan</code> and <code>Set::IntSpan::Fast</code> are similar. "Fast" does a binary search for member testing (not part of the task here). Both accept negatives.
25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
 
37, 38, 39;</lang>
=={{header|Phix}}==
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">spout</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">first</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">curr</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">sequence</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">res</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">first</span><span style="color: #0000FF;">=</span><span style="color: #000000;">curr</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"%d"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">first</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">else</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">sep</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">first</span><span style="color: #0000FF;">=</span><span style="color: #000000;">curr</span><span style="color: #0000FF;">-</span><span style="color: #000000;">2</span><span style="color: #0000FF;">?</span><span style="color: #008000;">','</span><span style="color: #0000FF;">:</span><span style="color: #008000;">'-'</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"%d%s%d"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">first</span><span style="color: #0000FF;">],</span><span style="color: #000000;">sep</span><span style="color: #0000FF;">,</span><span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">curr</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">res</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">extract_ranges</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">first</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">out</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)!=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">2</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]!=</span><span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]+</span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">out</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">spout</span><span style="color: #0000FF;">(</span><span style="color: #000000;">first</span><span style="color: #0000FF;">,</span><span style="color: #000000;">i</span><span style="color: #0000FF;">,</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)&</span><span style="color: #008000;">','</span>
<span style="color: #000000;">first</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">i</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #000000;">out</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">spout</span><span style="color: #0000FF;">(</span><span style="color: #000000;">first</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">out</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">r</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span><span style="color: #000000;">8</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">11</span><span style="color: #0000FF;">,</span><span style="color: #000000;">12</span><span style="color: #0000FF;">,</span><span style="color: #000000;">14</span><span style="color: #0000FF;">,</span><span style="color: #000000;">15</span><span style="color: #0000FF;">,</span><span style="color: #000000;">16</span><span style="color: #0000FF;">,</span><span style="color: #000000;">17</span><span style="color: #0000FF;">,</span><span style="color: #000000;">18</span><span style="color: #0000FF;">,</span><span style="color: #000000;">19</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">20</span><span style="color: #0000FF;">,</span><span style="color: #000000;">21</span><span style="color: #0000FF;">,</span><span style="color: #000000;">22</span><span style="color: #0000FF;">,</span><span style="color: #000000;">23</span><span style="color: #0000FF;">,</span><span style="color: #000000;">24</span><span style="color: #0000FF;">,</span><span style="color: #000000;">25</span><span style="color: #0000FF;">,</span><span style="color: #000000;">27</span><span style="color: #0000FF;">,</span><span style="color: #000000;">28</span><span style="color: #0000FF;">,</span><span style="color: #000000;">29</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">30</span><span style="color: #0000FF;">,</span><span style="color: #000000;">31</span><span style="color: #0000FF;">,</span><span style="color: #000000;">32</span><span style="color: #0000FF;">,</span><span style="color: #000000;">33</span><span style="color: #0000FF;">,</span><span style="color: #000000;">35</span><span style="color: #0000FF;">,</span><span style="color: #000000;">36</span><span style="color: #0000FF;">,</span><span style="color: #000000;">37</span><span style="color: #0000FF;">,</span><span style="color: #000000;">38</span><span style="color: #0000FF;">,</span><span style="color: #000000;">39</span><span style="color: #0000FF;">}</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">extract_ranges</span><span style="color: #0000FF;">(</span><span style="color: #000000;">r</span><span style="color: #0000FF;">)})</span>
<!--</syntaxhighlight>-->
{{out}}
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
=={{header|Phixmonti}}==
<syntaxhighlight lang="phixmonti">include ..\Utilitys.pmt
 
( ) var res
( )
( 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 )
 
def append
1 get swap -1 get rot swap
2 tolist res swap 0 put var res
enddef
 
def printRes
res len for
get
1 get swap 2 get nip
over over == if
drop print
else
over over - abs 1 > if "-" else "," endif
rot print print print
endif
"," print
endfor
drop
8 tochar print " " print
enddef
1 get rot swap 0 put swap
len 2 swap 2 tolist for
get var num
swap -1 get 1 + num != if
append
flush
endif
num 0 put swap
endfor
swap
append
 
clear
 
printRes</syntaxhighlight>
The same result in all examples.
{{out}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
A bit less ugly
 
<syntaxhighlight lang="phixmonti">include ..\Utilitys.pmt
 
( 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 )
 
len get var fin
2 var i
i get fin == not
while
i 1 - get var prev
i get prev - 1 != if
inf i put
i 1 + var i
endif
i 1 + var i
i get fin == not
endwhile
inf 0 put
 
def printEnd print "," print enddef
 
1 var ini
 
len for
var i
i get inf == if
i ini - ini swap slice
-1 get swap 1 get nip
over over == if drop printEnd
else over over - 1 == if printEnd printEnd
else print "-" print printEnd
endif
endif
i 1 + var ini
endif
endfor
8 tochar print " " print</syntaxhighlight>
 
Short version
 
<syntaxhighlight lang="phixmonti">include ..\Utilitys.pmt
 
( 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 )
 
inf 0 put
 
def printEnd print "," print enddef
 
2 var i
1 var ini
i get inf == not
while
i 1 - get var prev
i get var act
act prev - 1 != if
i ini -
dup 2 == if drop ini get prev swap printEnd printEnd else
dup 1 == if drop prev printEnd else
drop ini get print "-" print prev printEnd
endif
endif
i var ini
endif
i 1 + var i
act inf == not
endwhile
8 tochar print " " print</syntaxhighlight>
 
 
PicoLisp like version
 
<syntaxhighlight lang="phixmonti">include ..\Utilitys.pmt
 
def glue /# l o -- l #/
var ob
len 2 * 1 - 2 swap 2 3 tolist for
ob swap put
endfor
enddef
 
( )
 
( 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 )
 
len for drop
pop swap dup var N var M
len for drop
head M 1 + == if
pop swap var M
else
exitfor
endif
endfor
swap
N M == if N tostr 0 put else
N 1 + M == if N tostr 0 put M tostr 0 put else
N tostr "-" M tostr chain chain 0 put
endif
endif
swap
len 0 == if
drop
exitfor
endif
endfor
 
"," glue lprint</syntaxhighlight>
 
=={{header|Picat}}==
<syntaxhighlight lang="picat">go =>
Lists = [
[-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9,
10, 11, 14, 15, 17, 18, 19, 20],
[ 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..20,
[13],
[11,12,13,15]
].
foreach(List in Lists)
println(List),
println(make_ranges(List)),
nl
end,
nl.
 
 
make_ranges(L) = Res =>
Ranges = [],
Range = [L[1]],
 
% Identify the range
foreach(I in 2..L.length)
Li1 = L[I-1],
Li = L[I],
if Li == Li1+1 then
Range := Range ++ [Li]
else
if length(Range) > 0 then
Ranges := Ranges ++ [Range]
end,
Range := [] ++ [Li]
end
end,
% pickup the last range
if length(Range) > 0 then
Ranges := Ranges ++ [Range]
end,
Res := join([get_range(R) : R in Ranges], ",").
 
 
% Convert to range representation
get_range(R) =
cond(R.length == 1,
R.first().to_string(),
min(R).to_string() ++ "-" ++ max(R).to_string()).</syntaxhighlight>
 
{{out}}
<pre>[-6,-3,-2,-1,0,1,3,4,5,7,8,9,10,11,14,15,17,18,19,20]
-6,-3-1,3-5,7-11,14-15,17-20
 
[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
 
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
1-20
 
[13]
13
 
[11,12,13,15]
11-13,15</pre>
 
Output:
<pre>-6,-3-1,3-5,7-11,14,15,17-20
0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|PicoLisp}}==
<langsyntaxhighlight PicoLisplang="picolisp">(de rangeextract (Lst)
(glue ","
(make
Line 1,706 ⟶ 4,492:
((= N M) (link N))
((= (inc N) M) (link N M))
(T (link (list N '- M))) ) ) ) ) ) )</langsyntaxhighlight>
{{out}}
Output:
<pre>: (rangeextract
(0 1 2 4 6 7 8 11 12 14 15 16 17 18 19 20 21 22
Line 1,715 ⟶ 4,501:
 
=={{header|PL/I}}==
<syntaxhighlight lang="pli">/* Modified 19 November 2011 to meet requirement that there be at */
<lang PL/I>
/* Modified 19 November 2011 to meet requirement that there be at */
/* least 3 items in a run. */
range_extraction: /* 17 August 2010 */
Line 1,763 ⟶ 4,548:
c, d = ',';
end;
end range_extraction;</syntaxhighlight>
 
</lang>
OUTPUT 17/8/2010:
<syntaxhighlight lang="text">
0-2,4,6-8,11-12,14-25,27-33,35-39
</syntaxhighlight>
</lang>
{{out}}
OUTPUT 19/11/2011:
<langpre>
0-2,4,6-8,11,12,14-25,27-33,35-39
</langpre>
 
=={{header|PowerShell}}==
<syntaxhighlight lang="powershell">
function range-extraction($arr) {
if($arr.Count -gt 2) {
$a, $b, $c, $arr = $arr
$d = $e = $c
if((($a + 1) -eq $b) -and (($b + 1) -eq $c)) {
$test = $true
while($arr -and $test) {
$d = $e
$e, $arr = $arr
$test = ($d+1) -eq $e
}
if($test){"$a-$e"}
elseif((-not $arr) -and $test){"$a-$d"}
elseif(-not $arr){"$a-$d,$e"}
else{"$a-$d," + (range-extraction (@($e)+$arr))}
}
elseif(($b + 1) -eq $c) {"$a," + (range-extraction (@($b, $c)+$arr))}
else {"$a,$b," + (range-extraction (@($c)+$arr))}
} else {
switch($arr.Count) {
0 {""}
1 {"$arr"}
2 {"$($arr[0]),$($arr[1])"}
}
}
}
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)
</syntaxhighlight>
<b>Output:</b>
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
=={{header|Prolog}}==
Line 1,778 ⟶ 4,601:
The code uses three predicates '''extract_Range/2''', '''study_Range/2''' and '''pack_Range/2'''.<BR>
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.
<langsyntaxhighlight Prologlang="prolog">range_extract :-
L = [0, 1, 2, 4, 6, 7, 8, 11, 12, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
Line 1,867 ⟶ 4,690:
 
run(Val,[Other|RRest], [Val, Val],[Other|RRest]).
</syntaxhighlight>
</lang>
 
{{out}}
OutPut :
<pre>?- 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</pre>
 
=={{header|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:
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|Python}}==
===Procedural===
<lang python>def range_extract(lst):
====Python: for ordered sequences====
<syntaxhighlight lang="python">def range_extract(lst):
'Yield 2-tuple ranges or 1-tuple single elements from list of increasing ints'
lenlst = len(lst)
i, ranges = 0, []
while i< lenlst:
low = lst[i]
Line 1,978 ⟶ 4,728:
23, 24, 25, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39]]:
#print(list(range_extract(lst)))
printr(range_extract(lst))</langsyntaxhighlight>
 
{{out}}
Line 1,984 ⟶ 4,734:
0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
Output{{out}} if the <code>print'''r'''(...)</code> statement is commented-out instead of the <code>print(...)</code> statement directly above it.<br>
This shows the tuples yielded by generator function <code>range_extract</code>.
<pre>[(-8, -6), (-3, 1), (3, 5), (7, 11), (14,), (15,), (17, 20)]
[(0, 2), (4,), (6, 8), (11,), (12,), (14, 25), (27, 33), (35, 39)]</pre>
 
====Python: For ordered iterables====
A more general method that works on any sequential [https://docs.python.org/3/library/collections.abc.html?highlight=iterable#collections.abc.Iterable Iterable] of integers, not only [https://docs.python.org/3/library/collections.abc.html?highlight=iterable#collections.abc.Sequence Sequences]:
 
<syntaxhighlight lang="python">def range_extract(iterable):
'''Assumes iterable is sorted sequentially. Returns iterator of range tuples.'''
it = iter(iterable)
 
try:
i = next(it)
except StopIteration:
return
 
while True:
low = i
 
try:
j = next(it)
except StopIteration:
yield (low, )
return
while i + 1 == j:
i_next = j
try:
j = next(it)
except StopIteration:
yield (low, j)
return
i = i_next
 
hi = i
 
if hi - low >= 2:
yield (low, hi)
elif hi - low == 1:
yield (low,)
yield (hi,)
else:
yield (low,)
 
i = j
 
def printr(ranges):
print( ','.join( (('%i-%i' % r) if len(r) == 2 else '%i' % r)
for r in ranges ) )
 
if __name__ == '__main__':
for lst in [[-8, -7, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7,
8, 9, 10, 11, 14, 15, 17, 18, 19, 20],
[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(list(range_extract(lst)))
printr(range_extract(lst))</syntaxhighlight>
 
{{out}}
 
Identical to previous example.
 
====Python: Using push-able iterator====
Note that for an iterable yielding <code>1,2,3,6,7,8</code> the only way to determine the end of the first section of incremented numbers, <code>1,2,3</code> is to read the next number <code>6</code>, This next example defines an iterator where the <code>6</code> can be pushed back and so more cleanly made available for inclusion in detrmining the next sub-sequence of <code>6,7,8</code>.
 
<syntaxhighlight lang="python">class PushableIter():
"Can push items back on iterable"
def __init__(self, it):
self.it = iter(it)
self.pushed = []
 
def push(self, item):
self.pushed.append(item)
 
def pop(self):
return self.pushed.pop(0) if self.pushed else self.it.__next__()
 
def __iter__(self):
return self
 
def __next__(self):
return self.pop()
 
def range_extractp(sorted_iterable):
'Yield 2-tuple ranges or 1-tuple single elements from iter of increasing ints'
rest = PushableIter(sorted_iterable)
for this in rest:
low = hi = last = this
for nxt in rest: # Find upper range on incremented values
if nxt == last + 1:
last = hi = nxt
else: # Out of (sub)-range
rest.push(nxt)
break
if hi - low >= 2:
yield (low, hi)
elif hi - low == 1:
yield (low,)
yield (hi,)
else:
yield (low,)</syntaxhighlight>
{{out}}
When substituted for function <code>range_extract</code> in the first Python example it gives the same results.
 
===Composition of pure functions===
====Python: splitBy====
 
Defining a general and reusable '''splitBy''' function, which subdivides any list into groups at the points at which the relationship between consecutive items matches some binary predicate:
 
{{Trans|Haskell}}
{{Trans|JavaScript}}
{{Trans|AppleScript}}
{{Works with|Python|3.7}}
<syntaxhighlight lang="python">'''Range extraction'''
 
from functools import reduce
 
 
# rangeFormat :: [Int] -> String
def rangeFormat(xs):
'''Range-formatted display string for
a list of integers.
'''
return ','.join([
rangeString(x) for x
in splitBy(lambda a, b: 1 < b - a)(xs)
])
 
 
# rangeString :: [Int] -> String
def rangeString(xs):
'''Start and end of xs delimited by hyphens
if there are more than two integers.
Otherwise, comma-delimited xs.
'''
ys = [str(x) for x in xs]
return '-'.join([ys[0], ys[-1]]) if 2 < len(ys) else (
','.join(ys)
)
 
 
# TEST ----------------------------------------------------
# main :: IO ()
def main():
'''Test'''
 
xs = [
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(
__doc__ + ':\n[' + '\n'.join(map(
lambda x: ' ' + repr(x)[1:-1],
chunksOf(11)(xs)
)) + " ]\n\n -> '" + rangeFormat(xs) + "'\n"
)
 
 
# GENERIC -------------------------------------------------
 
# chunksOf :: Int -> [a] -> [[a]]
def chunksOf(n):
'''A series of lists of length n,
subdividing the contents of xs.
Where the length of xs is not evenly divible,
the final list will be shorter than n.'''
return lambda xs: reduce(
lambda a, i: a + [xs[i:n + i]],
range(0, len(xs), n), []
) if 0 < n else []
 
 
# splitBy :: (a -> a -> Bool) -> [a] -> [[a]]
def splitBy(p):
'''A list split wherever two consecutive
items match the binary predicate p.
'''
# step :: ([[a]], [a], a) -> a -> ([[a]], [a], a)
def step(acp, x):
acc, active, prev = acp
return (acc + [active], [x], x) if p(prev, x) else (
(acc, active + [x], x)
)
 
# go :: [a] -> [[a]]
def go(xs):
if 2 > len(xs):
return xs
else:
h = xs[0]
ys = reduce(step, xs[1:], ([], [h], h))
# The accumulated sublists, and the current group.
return ys[0] + [ys[1]]
 
return lambda xs: go(xs)
 
 
# MAIN ---
if __name__ == '__main__':
main()</syntaxhighlight>
{{Out}}
<pre>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 ]
 
-> '0-2,4,6-8,11,12,14-25,27-33,35-39'</pre>
 
=={{header|Qi}}==
<syntaxhighlight lang="qi">
<lang qi>
(define make-range
Start Start -> ["," Start]
Line 2,008 ⟶ 4,963:
25 27 28 29 30 31 32 33 35 36
37 38 39])
</syntaxhighlight>
</lang>
 
{{out}}
Output:
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
=={{header|R}}==
 
<syntaxhighlight lang="rsplus">extract.range = function(v) {
r <- c(1, which(diff(v) != 1) + 1, length(v) + 1)
paste0(collapse=",",
v[head(r, -1)],
ifelse(diff(r) == 1,
"",
paste0(ifelse(diff(r) == 2, ",", "-"),
v[r[-1] - 1])))
}
 
 
print(extract.range(c(
-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20)))
print(extract.range(c(
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)))</syntaxhighlight>
 
=={{header|Racket}}==
<syntaxhighlight lang="racket">
<lang Racket>
#lang racket
 
Line 2,031 ⟶ 5,005:
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"
</syntaxhighlight>
</lang>
 
=={{header|Raku}}==
(formerly Perl 6)
<syntaxhighlight lang="raku" line>sub range-extraction (*@ints) {
my $prev = NaN;
my @ranges;
 
for @ints -> $int {
if $int == $prev + 1 {
@ranges[*-1].push: $int;
}
else {
@ranges.push: [$int];
}
$prev = $int;
}
join ',', @ranges.map: -> @r { @r > 2 ?? "@r[0]-@r[*-1]" !! @r }
}
 
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;</syntaxhighlight>
 
{{out}}
<pre>-6,-3-1,3-5,7-11,14,15,17-20
0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|REXX}}==
Note that the two numbers &nbsp; '''11''' &nbsp; and &nbsp; '''12''' &nbsp; are not considered a range.
===version 1===
This REXX version isn't limited to integers. &nbsp; It doesn't need a magic number to terminate the list.
<syntaxhighlight lang="rexx">/*REXX program creates a range extraction from a list of integersnumbers (can be negative.) */
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
w#= words(old) /*number of integers in the number list.*/
new= /*the new list, (maybepossibly with ranges). */
do j=1 to w #; x z= word(old, j) /*get theobtain Jth number in the old list. */
newinc=new',' x1; new= new','z /*append Jth" " number to " new list. " */
inc=1; g= do k=j+1 to #; y= word(old, k) /*get the Kth number in the /*start with an increment ofnumber one.list*/
do k=j+1 to w; if y\=word(old,k)=z+inc then leave /*getis the Kththis number innot > previous theby list.inc?*/
if y\ inc=x+ inc + 1; g= y then leave /*isincrease thisthe numberrange, ¬>assign prev by incG ?(good).*/
inc=inc+1; g=y end /*increase range, assign g (good)k*/
if k-1=j | g=z+1 then iterate /*Is the range=0│1? Then keep truckin'*/
end /*k*/
ifnew= knew'-1=j'g; | g j=x+ k - 1 then iterate /*indicate a range= 0|1?of #s; Then keepchange truckin'index*/
new=new'-'g end /*indicate a range of numbers. j*/
j =k-1 /*changingstick thea fork Jin it, DO loopwe're indexall done. */
new= substr(new, 2) /*elide the leading comma in the range.*/
end /*j*/
say 'old:' old; say 'new:' new /*show the old and new range of numbers*/</syntaxhighlight>
 
{{out|output|text=&nbsp; when using the (internal) default list of numbers:}}
new=space(substr(new, 2), 0) /*elide leading comma, all blanks*/
say 'old:' old /*show the old range of numbers. */
say 'new:' new /*display new list of numbers. */
/*stick a fork in it, we're done.*/</lang>
'''output'''
<pre>
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
</pre>
<!-- elided version 1a as it was slower and less idiomatic.
 
===version 1a===
The REXX version is the same as above, but doesn't modify a &nbsp; '''DOdo''' &nbsp; loop's index &nbsp; ('''j''').,
<br>and it also doesn't need a magic number to terminate the list.
<lang>/*REXX program creates a range extraction from a list of integers. */
<syntaxhighlight lang="rexx">/*REXX program creates a range extraction from a list of numbers (can be negative.) */
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
w#= words(old); j= 0 /*number of integers in the number list.*/
new= /*the new list, (maybepossibly with ranges). */
do while j<w#; j= j + 1; x z= word(old, j) /*get the Jth number in the number list.*/
new inc=new',' x1; new= new','z /*append Jth number" " to " new list. " */
inc=1; g= do k=j+1 to #; y= word(old, k) /*get the Kth number in the /*start with an increment ofnumber one.list*/
do k=j+1 to w; if y\=word(old,k)=z+inc then leave /*getis the Kththis number innot > previous theby list.inc?*/
if y\ inc=x+ inc + 1; g= y then leave /*isincrease thisthe numberrange, ¬>assign prev by incG ?(good).*/
inc=inc+1; g=y end /*increase range, assign g (good)k*/
if k-1=j | g=z+1 then iterate /*Is the range=0│1? Then keep truckin'*/
end /*k*/
if k-1new=j | new'-'g; j=x+ k - 1 then iterate /*range=indicate 0|1?a range Thenof keepnumbers; truckin'change J*/
new=new'-'g end /*indicate a range of numbers. while*/
j =k-1 /*whichstick numbera tofork examinein next.it, we're all done. */
new= substr(new, 2) /*elide the leading comma in the list. */
end /*while*/
say 'old:' old; say 'new:' new /*show the old and new range of numbers*/</syntaxhighlight>
 
{{out|output|text=&nbsp; is the same as the 1<sup>st</sup> REXX version (1a).}}<br><br>
new=space(substr(new, 2), 0) /*elide leading comma, all blanks*/
!-->
say 'old:' old /*show the old range of numbers. */
say 'new:' new /*display new list of numbers. */
/*stick a fork in it, we're done.*/</lang>
'''output''' is the same as above.
 
===version 2===
Somewhat simplified !?!
<langsyntaxhighlight lang="rexx">/*REXX program to test range extraction. ******************************
* 07.08.2012 Walter Pachl
**********************************************************************/
Line 2,120 ⟶ 5,119:
End
Say 'new='ol
</syntaxhighlight>
</lang>
Output is the samesimilar as above.
 
=={{header|Ring}}==
<syntaxhighlight lang="ring">
# Project : Range extraction
 
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"
int = str2list(substr(int, ",", nl))
sumint = []
intnew = 1
for n=1 to len(int)
flag = 0
nr = 0
intnew = 0
for m=n to len(int)-1
if int[m] = int[m+1] - 1
intnew = m+1
flag = 1
nr = nr + 1
else
exit
ok
next
if flag = 1 and nr > 1
if intnew != 0
add(sumint, [n,intnew])
n = m
ok
else
add(sumint, [n,""])
ok
next
showarray(sumint)
 
func showarray(vect)
see "["
svect = ""
for n = 1 to len(vect)
if vect[n][2] != ""
svect = svect +"" + int[vect[n][1]] + "-" + int[vect[n][2]] + ", "
else
svect = svect +"" + int[vect[n][1]] + ", "
ok
next
svect = left(svect, len(svect) - 2)
see svect
see "]" + nl
</syntaxhighlight>
Output:
<pre>
[0-2, 4, 6-8, 11, 12, 14-25, 27-33, 35-39]
</pre>
 
=={{header|RPL}}==
« 1 SF "" <span style="color:grey">@ initialize output string</span>
'''WHILE''' OVER SIZE '''REPEAT''' <span style="color:grey">@ while input list is not empty</span>
'''IF''' 1 FC?C '''THEN''' "," + '''END''' <span style="color:grey">@ do not append comma at first iteration</span>
'''IFERR''' OVER ΔLIST '''THEN END''' <span style="color:grey">@ if at least 2 items in input list</span>
'''IF''' DUP 1 2 SUB { 1 1 } == '''THEN''' <span style="color:grey">@ if 3 consecutive items </span>
ROT ROT OVER HEAD + "-" + <span style="color:grey">@ append range start to result string</span>
ROT 1 « 1 ≠ » DOLIST 1 POS <span style="color:grey">@ get rank of last consecutive number</span>
'''IF''' DUP NOT '''THEN''' DROP OVER SIZE '''END'''
ROT DUP2 SWAP 1 + OVER SIZE SUB <span style="color:grey">@ remove processed items from input list</span>
4 ROLLD SWAP GET + <span style="color:grey">@ append range end to result </span>
'''ELSE''' <span style="color:grey">@ else</span>
DROP OVER HEAD + SWAP TAIL SWAP <span style="color:grey">@ append first item and remove it from input </span>
'''END'''
'''END'''
SWAP DROP
» '<span style="color:blue">→RNG</span>' STO
 
{ -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20 } <span style="color:blue">→RNG</span>
{{out}}
<pre>
1: "-6,-3-1,3-5,7-11,14,15,17-20"
</pre>
 
=={{header|Ruby}}==
<langsyntaxhighlight lang="ruby">def range_extract(l)
# pad the list with a big value, so that the last loop iteration will
sorted = l.sort
# append something to the range
range = []
sorted, range = l.sort.concat([Float::MAX]), []
start = sorted.first
canidate_number = sorted.first
# pad the list with a big value, so that the last loop iteration will
 
# appended something to the range
# enumerate over the sorted list in pairs of current number and next by index
sorted.concat([Float::MAX]).each_cons(2) do |prev,n|
sorted.each_cons(2) do |current_number, next_number|
if prev.succ < n
# if there is a gap between the current element and its next by index
if start == prev
if current_number.succ < next_number
range << start.to_s
# if current element is our first or our next by index
if canidate_number == current_number
# put the first element or next by index into our range as a string
range << canidate_number.to_s
else
range# <<if "%d%s%d"current %element [start,is (start.succnot ==the prevsame ?as ","the :first "-"),or prev]next
# add [first or next, first or next equals current add , else -, current]
seperator = canidate_number.succ == current_number ? "," : "-"
range << "%d%s%d" % [canidate_number, seperator, current_number]
end
# make the first element the next element
start = n
canidate_number = next_number
end
end
Line 2,150 ⟶ 5,232:
]
 
p rng = range_extract(lst)</langsyntaxhighlight>
 
{{out}}
output:
<pre>"0-2,4,6-8,11,12,14-25,27-33,35-39"</pre>
 
 
{{works with|Ruby|2.2}}
Enumerable#slice_when method became usable.
<syntaxhighlight lang="ruby">ary = [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]
puts ary.sort.slice_when{|i,j| i+1 != j}.map{|a| a.size<3 ? a : "#{a[0]}-#{a[-1]}"}.join(",")</syntaxhighlight>
 
{{out}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|Rust}}==
Iterators are very Rustic. This solution is generic for all numeric types.
<syntaxhighlight lang="rust">use std::ops::Add;
struct RangeFinder<'a, T: 'a> {
index: usize,
length: usize,
arr: &'a [T],
}
impl<'a, T> Iterator for RangeFinder<'a, T> where T: PartialEq + Add<i8, Output=T> + Copy {
type Item = (T, Option<T>);
fn next(&mut self) -> Option<Self::Item> {
if self.index == self.length {
return None;
}
let lo = self.index;
while self.index < self.length - 1 && self.arr[self.index + 1] == self.arr[self.index] + 1 {
self.index += 1
}
let hi = self.index;
self.index += 1;
if hi - lo > 1 {
Some((self.arr[lo], Some(self.arr[hi])))
} else {
if hi - lo == 1 {
self.index -= 1
}
Some((self.arr[lo], None))
}
}
}
impl<'a, T> RangeFinder<'a, T> {
fn new(a: &'a [T]) -> Self {
RangeFinder {
index: 0,
arr: a,
length: a.len(),
}
}
}
 
fn main() {
let input_numbers : &[i8] = &[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];
for (i, (lo, hi)) in RangeFinder::new(&input_numbers).enumerate() {
if i > 0 {print!(",")}
print!("{}", lo);
if hi.is_some() {print!("-{}", hi.unwrap())}
}
println!("");
}</syntaxhighlight>
 
{{out}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
Note: You could make the above solution even a little more generic in Nightly Rust (which is version 1.6 at the time of writing) by making the following additions:
 
Add this to the top of the file:
<syntaxhighlight lang="rust">#![feature(zero_one)]
use std::num::One;</syntaxhighlight>
 
Changing this line:
<syntaxhighlight lang="rust"> impl<'a, T> Iterator for RangeFinder<'a, T> where T: PartialEq + Add<i8, Output=T> + Copy {</syntaxhighlight>
to this:
<syntaxhighlight lang="rust">impl<'a, T> Iterator for RangeFinder<'a, T> where T: PartialEq + Add<T, Output=T> + Copy + One {</syntaxhighlight>
 
And this line:
<syntaxhighlight lang="rust"> while self.index < self.length - 1 && self.arr[self.index + 1] == self.arr[self.index] + 1 {</syntaxhighlight>
to this:
<syntaxhighlight lang="rust"> while self.index < self.length - 1 && self.arr[self.index + 1] == self.arr[self.index] + T::one() {</syntaxhighlight>
 
=={{header|Scala}}==
<langsyntaxhighlight lang="scala">object Range {
def spanRange(ls:List[Int])={
var last=ls.head
Line 2,180 ⟶ 5,346:
println(toRangeString(toRangeList(l)))
}
}</langsyntaxhighlight>
 
{{out}}
Output:
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|Scheme}}==
{{trans|Qi}}
<langsyntaxhighlight lang="scheme">
(define (make-range start end)
(cond ((= start end)
Line 2,216 ⟶ 5,382:
25 27 28 29 30 31 32 33 35 36
37 38 39))
</syntaxhighlight>
</lang>
 
{{out}}
Outputs:
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39
Line 2,224 ⟶ 5,390:
 
=={{header|Seed7}}==
<langsyntaxhighlight lang="seed7">$ include "seed7_05.s7i";
 
const func string: rangeExtraction (in array integer: numbers) is func
Line 2,255 ⟶ 5,421:
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;</langsyntaxhighlight>
 
{{out}}
Output:
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39
Line 2,270 ⟶ 5,436:
Handles +/- and negative ranges.
 
<langsyntaxhighlight SNOBOL4lang="snobol4">* # Absolute value
define('abs(n)') :(abs_end)
abs abs = ~(abs = lt(n,0) -n) n :(return)
Line 2,291 ⟶ 5,457:
+ '37, 38, 39'
output = rangext(test)
end</langsyntaxhighlight>
 
{{out}}
Output:
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|SQL}}==
{{works with|ORACLE 19c}}
This is not a particularly efficient solution, but it gets the job done.
 
<syntaxhighlight lang="sql">
/*
This code is an implementation of "Range extraction" in SQL ORACLE 19c
p_list_of_sets -- input string
delimeter by default ","
p_format -- output format:
0 => [-2,-1] [0,2]
1 => -2--1,0-2
*/
with
function range_extraction(p_list_of_sets in varchar2, p_format integer default 0)
return varchar2 is
--
v_list_of_sets varchar2(32767) := p_list_of_sets;
v_output varchar2(32767) ;
v_set_1 varchar2(2000) ;
v_set_2 varchar2(2000) ;
v_set_2_gr pls_integer;
v_max pls_integer;
--
function sort_set(p_in_str varchar2)
return varchar2 is
v_out varchar2(32767) := p_in_str;
begin
--
with out_tab as
(select distinct to_number(regexp_substr(str, '[^,]+', 1, rownum, 'c', 0) default null on conversion error ) elem
from
(select p_in_str as str
from dual
)
connect by level <= regexp_count(str, '[^,]+')
)
select distinct listagg(elem, ',') within group(order by elem) end
into v_out
from out_tab;
--
return v_out;
end;
--
begin
--cleaning
v_list_of_sets := replace(v_list_of_sets, ' ', '') ;
v_list_of_sets := sort_set(v_list_of_sets) ;
--
<<loop_through_set>>
while regexp_count(v_list_of_sets, '[^,]+') > 0
loop
v_set_1 := regexp_substr(v_list_of_sets, '[^,]+', 1, 1) ;
v_list_of_sets := regexp_replace(v_list_of_sets,v_set_1,'',1,1);
--
<<loop_for>>
for i in 1..regexp_count(v_list_of_sets, '[^,]+')
loop
v_set_2_gr := nvl(v_set_2,v_set_1);
v_set_2 := regexp_substr(v_list_of_sets, '[^,]+', 1, 1) ;
--
if to_number(v_set_2) > to_number(v_set_1) + i then
v_output := v_output||' ['||v_set_1||case when v_set_1 != v_set_2_gr then ','||v_set_2_gr end||']';
continue loop_through_set;
end if;
--
v_list_of_sets := regexp_replace(v_list_of_sets,v_set_2,'',1,1);
--
end loop loop_for;
--
v_output := v_output||' ['||v_set_1||case when v_set_1 != v_set_2 then ','||v_set_2 end||']';
v_list_of_sets := regexp_replace(v_list_of_sets,v_set_1,'',1,1);
--
end loop loop_through_set;
--
--output format
v_output := nvl(v_output,'[]');
if p_format = 1 then
v_output := ltrim(trim(v_output), '[');
v_output := rtrim(v_output, ']');
v_output := replace(v_output, ',', '-');
v_output := replace(v_output, '] [', ',');
end if;
--
return trim(v_output);
end;
 
--Test
select '-- Test, Standart Format ' as output from dual
union all
select lpad(', ',125) || ' ==> ' || range_extraction(', ') as output from dual
union all
select lpad('0,-1,2,-2',125) || ' ==> ' || range_extraction('0,-1,2,-2') as output from dual
union all
select lpad('3,3,0,0,-2,-2',125) || ' ==> ' || range_extraction('3,3,0,0,-2,-2') as output from dual
union all
select lpad('+0,-X,swde, 2q, +4, 3,0 ,-0,-2 , -3',125) || ' ==> ' || range_extraction('+0,-X,swde, 2q, +4, 3,0 ,-0,-2 , -3') as output from dual
union all
select lpad('-1,-11,-12,-14,-15,-16,-17,-18,-19,-2,-20,-21,-22,-23,-24,-25,-0,-27,-28,-29,-30,-31,-32,-33,-35,-36,-37,-38,-39,-4,-6,-7,-8',125) || ' ==> ' || range_extraction('-1,-11,-12,-14,-15,-16,-17,-18,-19,-2,-20,-21,-22,-23,-24,-25,-0,-27,-28,-29,-30,-31,-32,-33,-35,-36,-37,-38,-39,-4,-6,-7,-8') as output from dual
union all
select lpad('1,11,12,14,15,16,17,18,19,2,20,21,22,23,24,25,0,27,28,29,30,31,32,33,35,36,37,38,39,4,6,7,8',125) || ' ==> ' || range_extraction('1,11,12,14,15,16,17,18,19,2,20,21,22,23,24,25,0,27,28,29,30,31,32,33,35,36,37,38,39,4,6,7,8') as output from dual
union all
--Test RosettaCode
select '-- Test RosettaCode, Standart Format ' as output from dual
union all
select lpad('-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20',125) || ' ==> ' || range_extraction('-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20') as output from dual
union all
select lpad('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',125) || ' ==> ' || 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') as output from dual
union all
select '-- Test RosettaCode, RosettaCode Format' as output from dual
union all
select lpad('-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20',125) || ' ==> ' || range_extraction('-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20',1) as output from dual
union all
select lpad('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',125) || ' ==> ' || 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',1) as output from dual
;
</syntaxhighlight>
/
{{out}}
<pre>
-- Test, Standart Format
, ==> []
0,-1,2,-2 ==> [-2,0] [2]
3,3,0,0,-2,-2 ==> [-2] [0] [3]
+0,-X,swde, 2q, +4, 3,0 ,-0,-2 , -3 ==> [-3,-2] [0] [3,4]
-1,-11,-12,-14,-15,-16,-17,-18,-19,-2,-20,-21,-22,-23,-24,-25,-0,-27,-28,-29,-30,-31,-32,-33,-35,-36,-37,-38,-39,-4,-6,-7,-8 ==> [-39,-35] [-33,-27] [-25,-14] [-12,-11] [-8,-6] [-4] [-2,0]
1,11,12,14,15,16,17,18,19,2,20,21,22,23,24,25,0,27,28,29,30,31,32,33,35,36,37,38,39,4,6,7,8 ==> [0,2] [4] [6,8] [11,12] [14,25] [27,33] [35,39]
-- Test RosettaCode, Standart Format
-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20 ==> [-6] [-3,1] [3,5] [7,11] [14,15] [17,20]
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]
-- Test RosettaCode, RosettaCode Format
-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20 ==> -6,-3-1,3-5,7-11,14-15,17-20
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
</pre>
/
 
=={{header|Swift}}==
{{works with|Swift|3}}
 
<syntaxhighlight lang="swift">
import Darwin
 
func ranges(from ints:[Int]) -> [(Int, Int)] {
var range : (Int, Int)?
var ranges = [(Int, Int)]()
for this in ints {
if let (start, end) = range {
if this == end + 1 {
range = (start, this)
}
else {
ranges.append(range!)
range = (this, this)
}
}
else { range = (this, this) }
}
ranges.append(range!)
return ranges
}
func description(from ranges:[(Int, Int)]) -> String {
var desc = ""
for (start, end) in ranges {
desc += desc.isEmpty ? "" : ","
if start == end {
desc += "\(start)"
}
else if end == start + 1 {
desc += "\(start),\(end)"
}
else {
desc += "\(start)-\(end)"
}
}
return desc
}
let ex = [-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]
let longer = [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(description(from: ranges(from: ex)))
print(description(from: ranges(from: longer)))
</syntaxhighlight>
 
{{out}}
<pre>-6,-3-1,3-5,7-11,14,15,17-20
0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|Tailspin}}==
<syntaxhighlight lang="tailspin">
templates extract
templates out
when <{start: <=$.end::raw>}> do '$.start;' !
when <{end: <=$.start::raw+1>}> do '$.start;,$.end;' !
otherwise '$.start;-$.end;' !
end out
@: {start: $(1), end: $(1)};
[ $(2..last)... -> #, $@ -> out ] -> '$...;' !
when <=$@.end::raw+1> do @.end: $;
otherwise $@ -> out !
',' !
@: {start: $, end: $};
end 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 -> !OUT::write
</syntaxhighlight>
{{out}}
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
=={{header|Tcl}}==
<langsyntaxhighlight lang="tcl">proc rangeExtract list {
set result [lindex $list 0]
set first [set last [lindex $list 0]]
Line 2,325 ⟶ 5,711:
25 27 28 29 30 31 32 33 35 36
37 38 39
}]</langsyntaxhighlight>
{{out}}
Output:
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|TUSCRIPT}}==
<syntaxhighlight lang="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).
$$ MODE TUSCRIPT,{}
<lang tuscript>
$$ MODE TUSCRIPT
MODE DATA
$$ numbers=*
Line 2,339 ⟶ 5,725:
37, 38, 39
$$ MODE TUSCRIPT
numbers=EXCHANGE (numbers,":,><<>{0-00} :':")
unrangednrs=JOIN (numbers,"")
rangednrs=COMBINE (unrangednrs,"")
rangednrs=EXCHANGE (rangednrs,":':,:")
PRINT rangednrs
</syntaxhighlight>
</lang>
Output:
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
Solution without COMBINE
<langsyntaxhighlight lang="tuscript">
$$ MODE TUSCRIPT
MODE DATA
Line 2,382 ⟶ 5,769:
rangednrs=EXCHANGE (rangednrs,":':,:")
PRINT rangednrs
</syntaxhighlight>
 
{{out}}
 
 
</lang>
Output:
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39
</pre>
 
=={{header|TXR}}==
 
<syntaxhighlight lang="txrlisp">(defun range-extract (numbers)
`@{(mapcar [iff [callf > length (ret 2)]
(ret `@[@1 0]-@[@1 -1]`)
(ret `@{@1 ","}`)]
(mapcar (op mapcar car)
(split [window-map 1 :reflect
(op list @2 (- @2 @1))
(sort (uniq numbers))]
(op where [chain second (op < 1)])))) ","}`)</syntaxhighlight>
 
{{out|Run}}
 
<pre>$ txr
This is the TXR Lisp interactive listener of TXR 126.
Use the :quit command or type Ctrl-D on empty line to exit.
1> (load "range.tl")
nil
2> (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"</pre>
 
=={{header|UNIX Shell}}==
{{works with|bash}}
<langsyntaxhighlight lang="bash">#!/usr/bin/bash
 
range_contract () (
Line 2,422 ⟶ 5,828:
)
 
range_contract 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</langsyntaxhighlight>
{{out}}
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|Ursala}}==
<langsyntaxhighlight Ursalalang="ursala">#import std
#import int
 
Line 2,436 ⟶ 5,842:
#show+
 
t = <f x></langsyntaxhighlight>
{{out}}
output: <pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
<pre>0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
 
=={{header|VBAWren}}==
{{trans|Kotlin}}
<lang vb>
<syntaxhighlight lang="wren">var extractRange = Fn.new { |list|
Public Function RangeExtraction(AList) As String
if (list.isEmpty) return ""
'AList is a variant that is an array, assumed filled with numbers in ascending order
var sb = ""
Const RangeDelim = "-" 'range delimiter
var first = list[0]
Dim result As String
var prev = first
Dim InRange As Boolean
Dim Posn, ub, lb, rangestart, rangelen As Integer
 
var append = Fn.new { |index|
result = ""
if (first == prev) {
'find dimensions of AList
sb = sb + prev.toString
ub = UBound(AList)
} else if (first == prev - 1) {
lb = LBound(AList)
sb = sb + first.toString + "," + prev.toString
Posn = lb
} else {
While Posn < ub
sb = sb + first.toString + "-" + prev.toString
rangestart = Posn
rangelen = 0 }
if (index < list.count - 1) sb = sb + ","
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
 
for (i in 1...list.count) {
if (list[i] == prev + 1) {
prev = prev + 1
} else {
append.call(i)
first = list[i]
prev = first
}
}
append.call(list.count - 1)
return sb
}
 
var list1 = [-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]
Public Sub RangeTest()
System.print(extractRange.call(list1))
'test function RangeExtraction
var list2 = [0, 1, 2, 4, 6, 7, 8, 11, 12, 14,
'first test with a Variant array
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
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)
37, 38, 39]
Debug.Print "a) "; RangeExtraction(MyList)
System.print(extractRange.call(list2))</syntaxhighlight>
 
{{out}}
'next test with an array of integers
<pre>
Dim MyOtherList(1 To 20) As Integer
-6,-3-1,3-5,7-11,14,15,17-20
MyOtherList(1) = -6
0-2,4,6-8,11,12,14-25,27-33,35-39
MyOtherList(2) = -3
</pre>
MyOtherList(3) = -2
 
MyOtherList(4) = -1
=={{header|XPL0}}==
MyOtherList(5) = 0
XPL0 does not provide much in the way of string handling features. In
MyOtherList(6) = 1
this regard it's more like C than Basic. To overcome this limitation some
MyOtherList(7) = 3
questionable techniques (or downright nasty tricks) are used here.
MyOtherList(8) = 4
 
MyOtherList(9) = 5
Ordinarily, RangeExtract would simply output the result to the console
MyOtherList(10) = 7
and be done with it, but the task insists on having a string returned.
MyOtherList(11) = 8
Thus, instead of outputting to the console, which is device 0, it outputs
MyOtherList(12) = 9
to device 8, which is a buffer that can be written and read much like an
MyOtherList(13) = 10
ordinary file. It is read into String to meet the requirement.
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>
 
The zero-length String declaration works as long as there are no
Output:
variables declared after it that it can grow into. This must be true not
only in the RangeExtract function but also for any routines it calls. In
the case here all the routines called are "intrinsic" routines (such as
IntOut) that don't use the same memory space as XPL0 variables.
 
A safer possibility would have been to declare String globally with a
sufficiently large size, but that seemed less elegant.
 
Another limitation of XPL0 is that it is not able to determine the size
of an array, such as with a "sizeof List" command. Thus a sentinel (End)
is used. The -1>>1 provides the largest possible signed integer for both
the normal 32-bit integer versions of the language and for the older
16-bit versions.
 
An unusual feature of XPL0 is that it traditionally terminates strings by
setting the high bit of the last byte. The command "string 0" changes
this to terminate strings by appending a zero byte.
 
<syntaxhighlight lang "XPL0">
string 0;
def End = -1>>1;
 
func RangeExtract(List); \Return a string in the range format
int List, I, Lo, Hi;
char String(0);
[I:= 0;
loop [Lo:= List(I);
while List(I)+1 = List(I+1) do I:= I+1;
Hi:= List(I);
IntOut(8, Lo);
if Hi-Lo >= 2 then
[ChOut(8, ^-); IntOut(8, Hi)]
else if Hi-Lo = 1 then
[ChOut(8, ^,); IntOut(8, Hi)];
I:= I+1;
if List(I) = End then quit;
ChOut(8, ^,);
];
ChOut(8, 0);
I:= 0;
loop [String(I):= ChIn(8);
if String(I) = 0 then return String;
I:= I+1;
];
];
 
Text(0, 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, End]) )</syntaxhighlight>
{{out}}
<pre>
0-2,4,6-8,11,12,14-25,27-33,35-39</pre>
RangeTest
 
a) 0-2,4,6-8,11,12,14-25,27-33,35-39
=={{header|zkl}}==
b) -6,-3-1,3-5,7-11,14,15,17-20
<syntaxhighlight lang="zkl">fcn range(ns){
fcn(w){
if (w.atEnd) return(Void.Stop);
a:=b:=w.next(); n:=0;
while(b+1 == (c:=w.peekN(n))){ n+=1; b=c }
if(n>1){do(n){w.next()}; return("%d-%d".fmt(a,b)); }
a
} :
(0).pump(*,List,_.fp(ns.walker().tweak(Void,Void))).concat(",");
}</syntaxhighlight>
The trick here is to use a modified iterator,
one that can look past the end of the sequence without puking.
The function gathers three or more successive ints (saved as a "a-b" string list element) or just returns the first one (as a number) if it can't.
The resulting list is converted to strings separated by commas.
<syntaxhighlight lang="zkl">var ns=T(-6,-3,-2,-1,0,1,3,4,5,7,8,9,10,11,14,15,17,18,19,20);
range(ns).println();
 
ns=T(
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(ns).println();
 
range([1..100]).println();</syntaxhighlight>
 
{{out}}
<pre>
-6,-3-1,3-5,7-11,14,15,17-20
0-2,4,6-8,11,12,14-25,27-33,35-39
1-100
</pre>
1,150

edits