# integer() to CSV string with runs

I’m trying to convert any sorted (ascending) list of positive integers into a string of comma-separated values, including shorthand for “runs” of consecutive numbers more than 2 numbers long. A few examples:

Array( 1, 3, 5, 7, 8, 9 ) = “1,3,5,7-9”
Array( 4, 5, 11, 12, 15, 16, 17 ) = “4,5,11,12,15-17”
Array( 1, 2, 3, 4, 5, 6, 8, 11, 12, 23, 24, 25 ) = “1-6,8,11,12,23-25”

The string without runs is obviously very easy. Here’s my code that doesn’t work. It started out simply and I kept piling on more things. It’s obviously wrong logic, but I’m having trouble thinking in another way. Any help?

``````// a sorted integer array i() is given
' go through and remove indexes within a run
' look at 2 at a time and don't mark runs that are only 2 numbers long
dim j, n, thisA, thisB, lastValue as integer
dim r(), final as string
r.append str( i(0) )
lastValue = i(0)
j = 1
while j < i.Ubound
thisA = i(j)
thisB = i(j+1)
if thisA = lastValue + 1 and thisB = lastValue + 2 then
// RUN OF 2
if j = i.Ubound - 1 then
r.append "-"
r.append str( thisB )
else
r.append "-"
lastValue = thisB
end if
j = j + 2

elseif thisA = lastValue + 1 then
// RUN OF 1
if j = i.Ubound - 1 then
r.append str( thisA )
r.append str( thisB )
else
r.append str( thisA )
lastValue = thisA
end if
j = j + 1

else
// NO RUN
if j = i.Ubound - 1 then
r.append str( thisA )
r.append str( thisB )
else
r.append str( thisA )
lastValue = thisA
end if
j = j + 1

end if

wend
' always append the last value of an odd array (wrong)
if j = i.Ubound then r.append str( i(j) )

' horrible
final = ReplaceAll( join( r(), "," ), "-,-", "-" )
while instr( final, "-,-" ) > 0
final = ReplaceAll( final, "-,-", "-" )
wend
final = ReplaceAll( final, ",-,", "-" )
return final``````

quickly banged this out
its like RLE encoding ``````Public Function ToArrayString(values() as integer) as string
Dim runStarts() As Integer
Dim runLengths() As Integer

For i As Integer = 0 To values.ubound
If i = 0 Then
runStarts.Append i
runlengths.append 1
Continue
End If

If values(i) = values(runstarts(runstarts.ubound) + runLengths(runlengths.ubound) - 1) + 1 Then
runlengths(runLengths.ubound) = runlengths(runLengths.ubound) + 1
Continue
End If

runstarts.append i
runLengths.append 1
Next

Dim result As String

// now walk the starts And lengths To assemble our String
For i As Integer = 0 To runStarts.ubound

Dim startValue As Integer = values(runStarts(i))
Dim runLength As Integer = runLengths(i)

If result <> "" Then
result = result + ","
End If

If runLength = 1 Then
result = result + Str(startValue)

Elseif runlength = 2 Then
result = result + Str(startValue) + "," + Str(startValue+1)

Else
result = result + Str(startValue) + "-" + Str(startValue+runlength-1)

End If

Next

Return result
End Function
``````

``````Dim s As String
s = ToArrayString( Array( 1, 3, 5, 7, 8, 9 ))
If s <>  "1,3,5,7-9" Then
Break
End If

s = ToArrayString( Array( 4, 5, 11, 12, 15, 16, 17 ))
If s <> "4,5,11,12,15-17" Then
Break
End If

s = ToArrayString( Array( 1, 2, 3, 4, 5, 6, 8, 11, 12, 23, 24, 25 ))
If s <> "1-6,8,11,12,23-25"  Then
Break
End If``````

[code]Public Function ToArrayString(values() As Integer) As String

Var lastIndex, lastIndexMinus1 As Integer
lastIndex = values.LastRowIndex
lastIndexMinus1 = lastIndex - 1

Var finalString As String = “”
finalString = Str(values(0))

For i As Integer = 1 To lastIndexMinus1

If values(i) = values(i - 1) + 1 And values(i) = values(i + 1) - 1 Then
finalString = finalString + “-”
Else
finalString = finalString + “,” + Str(values(i))
End If

Next

finalString = finalString + “,” + Str(values(lastIndex))

finalString = finalString.ReplaceAll("-,", “-”)
While finalString.IndexOf("–") <> -1
finalString = finalString.ReplaceAll("–", “-”)
Wend

Return finalString

End Function[/code]

This was my approach:

``````Public Function ArrayToRange(a() As Integer) as String
if a.Ubound = -1 then return "" 'Bail out if null array
dim outList,seqDelimiter As String = ""
dim runCount As integer = 0
dim seqFlag As Boolean = false
for i as Integer = 0 to a.Ubound-1
if seqFlag and (a(i)<>a(i+1)-1) then 'Is this end of sequence?
outList = outList+seqDelimiter 'Yes, so add sequence delimiter
seqFlag = False 'reset flag and runCount
runCount = 0
end if
if not seqFlag then  'Is sequence in progress?
outList = outList+str(a(i)) 'No, so append the current value
end if
if a(i)=(a(i+1)-1) then 'Is this start or continuation of sequence?
seqFlag = true
runCount = runCount+1
seqDelimiter = if(runCount>1,"-",",")
Else 'Sequence not in progress
outList = outList+"," 'append a comma
end if
next
'Append last value and return
return outList+if(seqFlag,seqDelimiter,"")+str(a(a.Ubound))
End Function``````

Thank you guys! It is so nice to be able to get help here from people who are smarter than me!

Each solution works, obviously each differently, and I like each one for different reasons. The one closest to what I was originally going for is from Robert Livingston (which also makes me a little proud  inelegant string replacing at the end notwithstanding  since it’s also the shortest of the three posted answers ). Somehow I took a wrong turn and my code kept growing with more and more errors in it. Here’s my version of his code which I converted to the old API: