View Full Version : Concatenate Numbers
gmaxey
05-13-2013, 09:23 PM
Well I think that is the term. Paul, as the wizard in this area, or anyone else I hope you can help.
I've cobbled together some code to attempt to turn strings like 1, 2, 3, 4, 5, 10 into 1-5, 10. Can you advise if there is a better way? Thanks.
Sub TestProc()
MsgBox ConconcateNumbers("1, 2, 3, 4, 5, 7, 9, 12, 13, 14, 15, 16, 20, 21")
End Sub
Function ConconcateNumbers(ByRef strProcess As String) As String
Dim strNums() As String
Dim i As Long, j As Long
strNums() = Split(Replace(strProcess, " ", ""), ",")
strProcess = strNums(0)
For i = 1 To UBound(strNums)
On Error GoTo Err_Scope
If strNums(i) = strNums(i - 1) + 1 And strNums(i) = strNums(i + 1) - 1 Then
If Not Right(strProcess, 1) = "-" Then
strProcess = strProcess & "-"
Else
If strNums(i + 1) = strNums(i + 1 - 1) + 1 And strNums(i) = strNums(i + 1 + 1) - 1 Then
strProcess = strProcess & strNums(i)
End If
End If
Else
If Right(strProcess, 1) = "-" Then
strProcess = strProcess & strNums(i)
Else
strProcess = strProcess & ", " & strNums(i)
End If
End If
Err_Rentry:
Next i
lbl_Exit:
ConconcateNumbers = strProcess
Exit Function
Err_Scope:
If Right(strProcess, 1) = "-" Then
If strNums(i) < strNums(UBound(strNums)) - 1 Then
strProcess = strProcess & strNums(i)
Resume Err_Rentry
Else
strProcess = strProcess & strNums(UBound(strNums))
End If
Else
strProcess = strProcess & ", " & strNums(i)
End If
Resume lbl_Exit
End Function
fumei
05-13-2013, 09:58 PM
Greg, AFAIK there is only brute force testing each trailing character, which you have done nicely. I am going to carefully step through it, but it sure looks good to me.
Newbies and even those quite familiar with VBA, examine Greg's use of the sub-routine Err_Scope.
It may be nice to add anothert level of "error" trapping. If you add an alpha character to the mix, say the "h" in:
MsgBox ConconcateNumbers("1, 2, h3, 4, 5, 7, 9, 12, 13, 14, 15, 16, 20, 21")
It does not make any comment. It just returns:
1,2
and ("1, 2, 3, 4, 5, 7, 9, 12, 13, h14, 15, 16, 20, 21") returns:
1-5,7,9,12,13
Everything after the alpha character is ignored. I am not sure what the final objective. Say in the h14 example, would you want to just reject THAT, and end up with:
1-5,7,9,12,13,15,16,20,21
???????
fumei
05-13-2013, 10:05 PM
It is interesting that:
"1, 2, 3, 4, 5ggg, 7, 9, 12, 13, h14, 15, 16, 20, 21"
returns
1-3, 4
even though 4 DOES follow in sequence to 1,2,3
macropod
05-14-2013, 03:36 AM
The following is a function I developed to convert multiple sequences of 3 or more consecutive page numbers (hence the StrPages variable) in a list to a string consisting of the first & last numbers separated by a hyphen. The function includes some optional code to replace the final comma with, say, '&' or 'and'.
Function ParsePageRefs(StrPages As String , Optional StrEnd As String)
'This function converts multiple sequences of 3 or more consecutive numbers in a
' list to a string consisting of the first & last numbers separated by a hyphen.
Dim ArrTmp(), i As Integer, j As Integer, k As Integer
ReDim ArrTmp(UBound(Split(StrPages, ",")))
For i = 0 To UBound(Split(StrPages, ","))
ArrTmp(i) = Split(StrPages, ",")(i)
Next
For i = 0 To UBound(ArrTmp) - 1
If IsNumeric(ArrTmp(i)) Then
k = 2
For j = i + 2 To UBound(ArrTmp)
If CInt(ArrTmp(i) + k) <> CInt(ArrTmp(j)) Then Exit For
ArrTmp(j - 1) = ""
k = k + 1
Next
i = j - 1
End If
Next
StrPages = Replace(Replace(Replace(Replace(Replace(Join(ArrTmp, ","), ",,", " "), " ,", " "), " ", " "), " ", "-"), ",", ", ")
If StrEnd <> "" Then
i = InStrRev(StrPages, ",")
If i > 0 Then
StrPages = Left(StrPages, i - 1) & Replace(StrPages, ",", " " & Trim(StrEnd), i)
End If
End If
ParsePageRefs = StrPages
End Function
The following macro demonstrates the function.
Sub Test()
Dim StrPages As String
StrPages = "1,2,3,4,5,7,8,9,11,13,14,15,16"
MsgBox ParsePageRefs(StrPages, "&")
End Sub
gmaxey
05-14-2013, 09:52 AM
Paul, Gerry
Thanks for your interest and input to this thread.
I had made several attempts at something like Paul provided but the clumps of hair and bloody scalp was piling up so I rerouted to the code I posted. Paul your code gave me some ideas which are provided below.
One problem I encountered with Paul's code was with long sequences of continuous numbers (e.g, 1,2,3,4,5,6,10,30,31,32,33,34). When running the code if there were more than four (I think) then I was get multiple "-" in the string. Other than continuing to add to the Replace(Replace), I devised a different method that at least appears to work. I also added some more validation of the string passed.
Function ParseNumbers(strNums As String, Optional strFinalSep As String) As String
'Converts multiple sequences of 3 or more consecutive numbers in a list to a string
'consisting of the first & last numbers separated by a hyphen.
Dim ArrTmp() As String, strValid As String
Dim lngIndex As Long, lngAdv As Long, lngOffset As Long
'Create array of numbers passed.
ArrTmp = Split(Replace(strNums, " ", ""), ",")
'Validate string passed.
strValid = Join(ArrTmp, "")
'Does string consist of whole numbers only?
If InStr(strValid, ".") > 0 Or InStr(strValid, "$") > 0 Or Not IsNumeric(strValid) Then
ParseNumbers = "Invalid string. The string passed must be whole numbers only."
Exit Function
End If
'Are whole numbers increasing in sequence?
For lngIndex = 0 To UBound(ArrTmp) - 1
If CLng(ArrTmp(lngIndex)) >= CLng(ArrTmp(lngIndex + 1)) Then
ParseNumbers = "Invalid string. The string passed must be whole numbers increasing in sequence."
Exit Function
End If
Next lngIndex
'Parse each number except the last number.
For lngIndex = 0 To UBound(ArrTmp) - 1
lngOffset = 2
For lngAdv = lngIndex + 2 To UBound(ArrTmp)
'Does the offset number element match the next expected number in a continous sequence?
If ArrTmp(lngIndex) + lngOffset <> ArrTmp(lngAdv) Then
Exit For
Else
ArrTmp(lngAdv - 1) = ""
lngOffset = lngOffset + 1
End If
Next
'Revise indexing.
lngIndex = lngAdv - 2
Next
'Format the output string.
strNums = ""
For lngIndex = 0 To UBound(ArrTmp)
If ArrTmp(lngIndex) <> "" Then
'Does this number end a conconated number sequence?
If Right(strNums, 1) = "-" Then
strNums = strNums & ArrTmp(lngIndex)
Else
On Error Resume Next
If ArrTmp(lngIndex) - 1 <> ArrTmp(lngIndex - 1) Then
'Is this the first pass? Yes, then expect error due to scope conflict.
If Err.Number <> 0 Then
'Define first number.
strNums = strNums & ArrTmp(lngIndex)
Else
'Start new sequence.
strNums = strNums & ", " & ArrTmp(lngIndex)
End If
Else
'Two numbers in sequence. List both.
strNums = strNums & ", " & ArrTmp(lngIndex)
End If
End If
Else
If Not Right(strNums, 1) = "-" Then
'Flag in conconated number sequence.
strNums = strNums & "-"
End If
End If
Next lngIndex
'Format optional final separator.
If strFinalSep <> "" Then
lngIndex = InStrRev(strNums, ",")
If lngIndex > 0 Then
strNums = Left(strNums, lngIndex - 1) & Replace(strNums, ",", " " & Trim(strFinalSep), lngIndex)
End If
End If
ParseNumbers = strNums
End Function
Tested with:
Sub Test()
Dim StrPages As String
StrPages = "9, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 30"
MsgBox ParseNumbers(StrPages, "")
StrPages = "1,2,3,5,6,7,8,10,11,17,18,19,20,21,22,23,30"
MsgBox ParseNumbers(StrPages, " and ")
StrPages = "1,2,4,5,6"
MsgBox ParseNumbers(StrPages, "")
StrPages = "1,2,3,4,5"
MsgBox ParseNumbers(StrPages, "")
StrPages = "1,2h,3,4,5"
MsgBox ParseNumbers(StrPages, "")
StrPages = "1,2.5,3,4,5"
MsgBox ParseNumbers(StrPages, "")
StrPages = "1,$2,3,4,5"
MsgBox ParseNumbers(StrPages, "")
StrPages = "1,2,4,5,5"
MsgBox ParseNumbers(StrPages, "")
End Sub
macropod
05-14-2013, 09:34 PM
Hi Greg,
I hadn't noticed the '--' occurrences before, probably because I hadn't tested with long-enough sequences. The following update should take care of that:
Function ParsePageRefs(StrPages As String, Optional StrEnd As String)
'This function converts multiple sequences of 3 or more consecutive numbers in a
' list to a string consisting of the first & last numbers separated by a hyphen.
Dim ArrTmp(), i As Integer, j As Integer, k As Integer
ReDim ArrTmp(UBound(Split(StrPages, ",")))
For i = 0 To UBound(Split(StrPages, ","))
ArrTmp(i) = Split(StrPages, ",")(i)
Next
For i = 0 To UBound(ArrTmp) - 1
If IsNumeric(ArrTmp(i)) Then
k = 2
For j = i + 2 To UBound(ArrTmp)
If CInt(ArrTmp(i) + k) <> CInt(ArrTmp(j)) Then Exit For
ArrTmp(j - 1) = ""
k = k + 1
Next
i = j - 2
End If
Next
StrPages = Join(ArrTmp, ",")
StrPages = Replace(Replace(Replace(StrPages, ",,", " "), ", ", " "), " ,", " ")
While InStr(StrPages, " ")
StrPages = Replace(StrPages, " ", " ")
Wend
StrPages = Replace(Replace(StrPages, " ", "-"), ",", ", ")
If StrEnd <> "" Then
i = InStrRev(StrPages, ",")
If i > 0 Then
StrPages = Left(StrPages, i - 1) & Replace(StrPages, ",", " " & Trim(StrEnd), i)
End If
End If
ParsePageRefs = StrPages
End Function
gmaxey
05-14-2013, 10:00 PM
Paul,
Yes that seems to work just as well as my version. At least with the limited test strings. Do you even own a sledgehammer? You have a gift for driving railroad spikes with a tack hammer. Well as least there are two working versions posted here.
The purpose behind this exercise was to figure out a way to concenate a series of rather odd sequence fields nested in a content control. If you are interested here is the code I used:
Sub ScratchMacro()
'A basic Word macro coded by Greg Maxey
Dim oCC As ContentControl
Dim strNums As String
Dim arrParsedNums() As String
Dim i As Long
Dim oFld As Word.Field
Set oCC = Selection.ContentControls(1)
strNums = oCC.Range.Text
For i = 1 To Len(strNums)
If IsNumeric(Mid(strNums, i, 1)) Then
strNums = Right(strNums, Len(strNums) - i + 1)
Exit For
End If
Next i
'This gives us the number string
Debug.Print strNums
strNums = ParseNumbers(strNums)
Debug.Print strNums
arrParsedNums = Split(strNums, ",")
i = 0
Dim oRng As Word.Range
For Each oFld In oCC.Range.Fields
If oFld.Type = wdFieldSequence Then
oFld.Code.Text = Replace(oFld.Code.Text, "\* MERGEFORMAT", "")
oFld.Code.Text = Replace(oFld.Code.Text, "\* MergeFormat", "")
oFld.Code.Text = Replace(oFld.Code.Text, " ", " ")
oFld.Code.Text = Replace(oFld.Code.Text, " ", " ")
If arrParsedNums(i) = "X" Then
oFld.Code.Text = oFld.Code.Text & "\h"
'oFld Update triggers CC entry event
ActiveDocument.Fields.Update
End If
i = i + 1
End If
Next oFld
For i = 2 To oCC.Range.Characters.count
If IsNumeric(oCC.Range.Characters(i)) Then
Set oRng = oCC.Range.Characters(i)
Exit For
End If
Next i
Do Until oRng.End = oCC.Range.End
oRng.Move wdCharacter, 1
Select Case True
Case oRng.Characters(1) = "," And oRng.Characters(1).Next = ","
oRng.Characters(1) = "-"
Case oRng.Characters(1).Previous = "-" And oRng.Characters(1) = ","
oRng.Characters(1).Delete
End Select
Loop
For Each oFld In oCC.Range.Fields
If Trim(oFld.Code.Text) = "Macrobutton PT_Haz [Edit]" Then
oFld.Code.Text = "Macrobutton PT_Haz"
End If
Exit For
Next
End Sub
Function ParseNumbers(strNums As String) As String
'Converts multiple sequences of 3 or more consecutive numbers in a list to a string
'consisting of the first & last numbers separated by a hyphen.
Dim arrNums() As String, strValid As String
Dim lngIndex As Long, lngAdv As Long, lngOffset As Long
'Create array of numbers passed.
arrNums = Split(Replace(strNums, " ", ""), ",")
'Parse each number except the last number.
For lngIndex = 0 To UBound(arrNums) - 1
lngOffset = 2
For lngAdv = lngIndex + 2 To UBound(arrNums)
'Does the offset number element match the next expected number in a continous sequence?
If arrNums(lngIndex) + lngOffset <> arrNums(lngAdv) Then
Exit For
Else
arrNums(lngAdv - 1) = "X"
lngOffset = lngOffset + 1
End If
Next
'Revise indexing.
lngIndex = lngAdv - 2
Next
strNums = ""
For lngIndex = 0 To UBound(arrNums)
strNums = strNums & "," & arrNums(lngIndex)
Next lngIndex
strNums = Right(strNums, Len(strNums) - 1)
ParseNumbers = strNums
End Function
The expanded field code in the CC might look like this:
{ Macrobutton PT_Haz}/ { SEQ TableHazard HazardTableUnique1 },{ SEQ TableHazard HazardTableUnique2},{ SEQ TableHazard HazardTableUnique3}
the result will look like this:
{MacroButton PT_Haz}/ { SEQ TableHazard HazardTableUnique1 }-{ SEQ TableHazard HazardTableUnique2 \h}{ SEQ TableHazard HazardTableUnique3}
Thanks again for you interest!
macropod
05-14-2013, 10:06 PM
Do you even own a sledgehammer? You have a gift for driving railroad spikes with a tack hammer.
No, but I do own a crowbar ... not so sure about the 'gift', though - I don't own a tack hammer either.
PS: I've streamlined the code a bit since the initial post.
gmaxey
05-14-2013, 10:11 PM
Well you've "tacked" a plenty of fine code here and elsewhere so let me say that you are very handy with that crowbar.
gmaxey
05-15-2013, 03:31 PM
Taking back the other direction:
Function ExpandTruncNums(ByRef strTruncNums As String) As String
'A basic Word macro coded by Greg Maxey
Dim strNumList, lngStart As Long, lngEnd As Long
Dim arrNums() As String
Dim i As Long, j As Long
strTruncNums = Replace(Replace(strTruncNums, " ", ""), "and", ",")
arrNums() = Split(strTruncNums, ",")
For i = 0 To UBound(arrNums)
Select Case True
Case InStr(arrNums(i), "-") > 0
lngStart = VBA.Left(arrNums(i), InStr(arrNums(i), "-") - 1)
lngEnd = VBA.Mid(arrNums(i), InStr(arrNums(i), "-") + 1, Len(arrNums(i)) - InStr(arrNums(i), "-"))
For j = lngStart To lngEnd
strNumList = strNumList & j & ", "
Next j
Debug.Print arrNums(i)
Case Else
strNumList = strNumList & arrNums(i) & ", "
End Select
Next i
ExpandTruncNums = Left(strNumList, Len(strNumList) - 2)
lbl_Exit:
Exit Function
End Function
macropod
05-16-2013, 02:25 AM
Wouldn't it have been easier to not do the previous concatenation? :whip
gmaxey
05-16-2013, 05:11 AM
But not as much fun!
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions Inc. All rights reserved.