Excel VBA - For/While Loop in a Node-Path Problem

So,

I have a set of string (Connector_String) which contains strings (which represent network-like nodes connections) that show all possible connections. The Connector_String has the following format (which I thought would help me but I can change it if needed):

  • Starts and ends with "-"
  • The nodes connected (always 2) represented as String1*String2
  • The node before "*" indicate the direction. So, for that above, direction is String1 --> String2
  • Connected nodes separated by "-"

For instance, -RANDIAC*RANDACBD-RANDV*RANDIF-...-RANDA*RANDACAC- Which means RANDIAC connects with RANDACBD etc. Also note that RANDIAC could be connected with another node.

I'm trying to list all possible path between the nodes given a starting and ending point. For that, I have two strings which include all the starting (Start_String) and ending nodes (End_String). The format is as follow: -RAND26RD-RAND06RD-...-RAND12RD-

I started writing a for loop code to iterate through the Connector_String but I soon realized that I have to write the same loop many times (which I do not know how to define how many). I then wrote a Do While loop code (first time ever for me to use it) which ended up not running at all (I don't understand why). Then, I tried to write a Function with the same for loop I use on the Sub and then run the Function in the Sub and in the 'Function' (hoping that will do the same job as the Do While loop).

None of my code have worked, but I'm adding my last attempt as it is recommended to have it on the question (although I doubt if an experienced person will read it as it is not well written - plus doesn't work).

Public Function Str_Search(a As String) As String
    
    Dim i As Long
    Debug.Print "Func " & a
    If InStr(End_Str, Split(a, "-")(UBound(Split(a, "-")))) > 0 Then
        Str_Search = a
        Exit Function
    End If
    
    For i = 1 To UBound(Split(Connector_String, "-")) - 1
        If Split(a, "-")(UBound(Split(a, "-"))) = Split(Split(Connector_String, "-")(i), "*")(0) Then
            a = a & "-" & Split(Split(Connector_String, "-")(i), "*")(1)
            Str_Search (a)
        End If
    Next i
    
End Function

Sub test_V4()
    Dim a As String
    Dim i As Long
    
    a = ""
    
    For i = 1 To UBound(Split(Connector_String, "-")) - 1
        If InStr(Start_String, Split(Split(Connector_String, "-")(i), "*")(0)) > 0 Then
            a = Replace(Split(Connector_String, "-")(i), "*", "-")
        ElseIf a <> "" Then
            Str_Search (a)
        ElseIf InStr(End_String, Split(a, "-")(UBound(Split(a, "-")))) > 0 Then
            Exit Sub
        End If
    Next

End Sub

Lastly, another tricky problem with my nodes is that there are nodes are bidirectional (so, I might have String1*String2 and String2*String1), which impose the problem to create infinite loop (I haven't not try to address that on my code as I can't even get a few paths).

See below the strings:

Start_String

-RAND_VW_E-RAND_VG_E-RAND_VG_F-RAND_M_2C-RAND_M_3A-RAND_VW_D-RAND_VW_V-RAND_M_1E-RAND_M_4E-RAND_VG_V-RAND_M_2D-RAND_M_3B-RAND_VW_C-RAND_VG_D-RAND_M_1F-RAND_M_4F-RAND_I_LINE-RAND_M_1E & RAND_M_4E-RAND_M_1F & RAND_M_4F

End_String

-RAND26RD-RAND06RD-RAND08RD-RAND12RD-RAND06RD-RAND02RD-RAND07RD-RAND01RD-RAND05RD-RAND03RD-RAND09RD-RAND04RD-RAND10RD-RAND20RD-RAND21RD-RAND22RD-RAND23RD-

Connector_String

-RANDIAC*RANDACBD-RANDV*RANDIF-RANDV*RANDIBD-RANDBD*RAND26RD-RANDACBD*RANDBD-RAND67F*RAND06RD-RAND89AC*RAND08RD-RANDACAC*RAND89AC-RANDA*RANDACAC-RAND_VW_E*RANDE-RAND_VG_E*RANDE-RAND_VG_F*RANDF-RAND_M_2C*RANDC-RAND_M_3A*RANDA-RANDEBD*RANDBD-RANDE*RANDEBD-RANDI*RANDIBD-RANDIBD*RANDBD-RANDF*RANDFNTH-RANDACAC*RANDACBD-RAND_VW_D*RANDD-RANDFSTH*RAND12F-RAND12F*RAND12RD-RANDIAC*RAND67AC-RAND67AC*RAND06RD-RANDFSTH*RAND02F-RAND02F*RAND02RD-RAND_VW_V*RANDV-RANDE*RANDEF-RAND_M_1E*RANDE-RAND_M_4E*RANDE-RANDEF*RANDFSTH-RAND_VG_V*RANDV-RANDV*RANDIAC-RANDFSTH*RAND67F-RAND67F*RAND07RD-RANDFNTH*RAND01RD-RANDIF*RANDFSTH-RANDB*RANDBD-RAND_M_2D*RANDD-RAND_M_3B*RANDB-RANDI*RANDIF-RANDIF*RANDFNTH-RANDFNTH*RAND05RD-RANDC*RANDACAC-RAND_VW_C*RANDC-RANDACAC*RAND67AC-RAND67AC*RAND07RD-RAND_VG_D*RANDD-RANDD*RANDBD-RAND_M_1F*RANDF-RAND_M_4F*RANDF-RANDFSTH*RAND03F-RAND03F*RAND03RD-RANDI*RANDIAC-RAND_I_LINE*RANDI-RANDIAC*RAND89AC-RAND89AC*RAND09RD-RANDF*RANDFSTH-RANDFSTH*RAND0410-RAND0410*RAND04RD-RAND0410*RAND10RD-RANDBD*RAND26BD-RANDFSTH*RANDFWST-RANDFWST*RANDFX-RAND20X*RAND20RD-RAND21X*RAND21RD-RANDFX*RAND21X-RANDFX*RAND20X-RANDEF*RANDFNTH-RANDACAC*RANDJET-RAND22Y*RAND22RD-RAND23Y*RAND23RD-RANDACY*RAND23Y-RANDJET*RANDACY-RANDACY*RAND22Y-RAND23Y*RAND23BD-RAND22Y*RAND22BD-RAND22Y*RAND23BD-RAND26BD*RAND22BD-RAND26BD*RAND23BD-RAND23BD*RAND26BD-RAND22BD*RAND26BD-RAND23BD*RAND23RD-RAND22BD*RAND22RD-RAND26BD*RAND26RD-RANDJET*RANDACX-RANDACX*RAND20X-RANDACX*RAND21X-RANDACX*RANDFX-RANDFX*RANDFWST-RANDFWST*RANDFSTH-RANDFSTH*RANDFNTH-

Hopefully, someone can help me with that.

enter image description here


Solution 1:

Copy the connections to a text file named Connector.txt and save in the same folder as the workbook. Connections are written to Sheet1 and routes to Sheet2. Routes are traced using the dictionary dict built from the connector file. The route array stores nodes as it recurses along the path. End points are highlighted in yellow.

Option Explicit 
Dim dictEnd As Object 
Dim dict As Object

Sub Str_Search()

    Const CONFILE = "Connector.txt"
    
    ' dictionaries
    Set dictEnd = CreateObject("Scripting.Dictionary")
    Call EndNodes(dictEnd)
    'MsgBox Join(dictEnd.keys, vbLf)

    Set dict = CreateObject("Scripting.Dictionary")
    Call ConnectedNodes(dict, ThisWorkbook.Path & "\" & CONFILE)
    ' dump source to check
    Call DumpConnected(Sheet1, dict)
    
    ' trace routes to sheet2
    Const STEPS = 20
    Dim route(1 To STEPS) As String, arStart, k
    Dim n As Long, r As Long
    r = 2
    arStart = StartNodes()
    With Sheet2
        .Cells.Clear
        .Cells(1, 1) = "Start Node"
        For n = 0 To UBound(arStart)
            k = arStart(n)
            If dict.exists(k) Then
                route(1) = k
                Call TraceRoute(route, 1, r, Sheet2)
                r = r + 1
            ElseIf Len(k) > 0 Then
               MsgBox k & " not found", vbCritical
            End If
        Next
        .Columns.AutoFit
    End With
    MsgBox "Done", vbInformation
    
End Sub

Sub TraceRoute(ByRef route, ByRef i As Long, ByRef r As Long, ws As Worksheet)

    'Debug.Print r, i, route(i)
    Dim node As String, dest As String
    Dim n As Long, j As Long, msg As String
    
    ' current node
    node = route(i)
    ws.Cells(r, i) = node
    
    ' is end node
    If dictEnd.exists(node) Then
        ws.Cells(r, i).Interior.Color = RGB(255, 255, 0)
    End If
        
    ' check not infinite loop
    For j = 1 To i - 1
        If route(j) = node Then
            msg = "Inf Loop "
            ws.Cells(r, i + 1) = msg
            r = r + 1
            Exit Sub
        End If
    Next
    
    ' end of route ?
    If Not dict.exists(node) Then
        r = r + 1
        Exit Sub
    End If
    
    msg = ""
    For n = 1 To dict(node).Count
        dest = dict(node).Item(n)
        
        ' recurse
        If dict.exists(dest) Then
            
            i = i + 1
            route(i) = dest
            Call TraceRoute(route, i, r, ws)
            i = i - 1
        Else
            ws.Cells(r, i + 1) = dest
            If dictEnd.exists(dest) Then
                ws.Cells(r, i + 1).Interior.Color = RGB(255, 255, 0)
            End If
            r = r + 1
        End If
    Next
    
End Sub

Function StartNodes() As Variant
    StartNodes = Split("-RAND_VW_E-RAND_VG_E-RAND_VG_F-RAND_M_2C-RAND_M_3A-RAND_VW_D-RAND_VW_V" & _
    "-RAND_M_1E-RAND_M_4E-RAND_VG_V-RAND_M_2D-RAND_M_3B-RAND_VW_C-RAND_VG_D" & _
    "-RAND_M_1F-RAND_M_4F-RAND_I_LINE-RAND_M_1E-RAND_M_4E-RAND_M_1F-RAND_M_4F", "-")
End Function

Sub EndNodes(ByRef d)
    Dim k
    For Each k In Split("-RAND26RD-RAND06RD-RAND08RD-RAND12RD-RAND06RD-RAND02RD-RAND07RD-RAND01RD" & _
    "-RAND05RD-RAND03RD-RAND09RD-RAND04RD-RAND10RD-RAND20RD-RAND21RD-RAND22RD-RAND23RD-", "-")
        If Len(Trim(k)) > 0 Then d(Trim(k)) = 1
    Next
    MsgBox d.Count & " End Nodes"
End Sub

Sub ConnectedNodes(ByRef d, filename As String)

    ' read connection file
    Dim FSO As Object, ts As Object, sTxt As String
    Set FSO = CreateObject("Scripting.FilesystemObject")
    Set ts = FSO.OpenTextFile(filename)
    sTxt = ts.readAll
    ts.Close
    
    ' regular expression
    Dim regex As Object, m As Object, node As Object
    Dim n As Long, k
    
    Set regex = CreateObject("vbscript.regexp")
    With regex
        .Global = True
        .MultiLine = True
        .IgnoreCase = True
        .Pattern = "(?:-([^*]+)\*([^-]+))"
    End With
    
    ' parse file
    If regex.test(sTxt) Then
        Set m = regex.Execute(sTxt) '
        For n = 1 To m.Count
            Set node = m.Item(n - 1).submatches
            k = Trim(node(0))
            If Not dict.exists(k) And Len(k) > 0 Then
                dict.Add k, New Collection
            End If
            dict(k).Add Trim(node(1))
        Next
    End If
    MsgBox d.Count & " Connectd Nodes"
End Sub

Sub DumpConnected(ws As Worksheet, dict)

    Dim k, r As Long, n As Long
    r = 1
    With ws
        .Cells.Clear
        .Cells(r, 1) = "Start Node"
        For Each k In dict
            r = r + 1
            .Cells(r, 1) = k
            For n = 1 To dict(k).Count
                .Cells(r, n + 1) = dict(k).Item(n)
            Next
        Next
        .Columns.AutoFit
        
        .Sort.SortFields.Clear
        .Sort.SortFields.Add Key:=.Range("A1"), SortOn:=xlSortOnValues, _
            Order:=xlAscending, DataOption:=xlSortNormal
        With .Sort
            .SetRange ws.UsedRange
            .Header = xlYes
            .MatchCase = False
            .Orientation = xlTopToBottom
            .SortMethod = xlPinYin
            .Apply
        End With
    End With

End Sub