Using a recursive NavigateArrows Method to analyse all local and off sheet cell dependencies

AID: 2762
  • Status: Published

6230 points

  • Bybrettdj
  • TypeTips/Tricks
  • Posted on2010-03-28 at 19:06:26
Awards
  • Community Pick
  • Experts Exchange Approved

Introduction


I do apologise for the academic sounding title, I simply couldn't find a better descriptor without the jargon. :)

This article revisits one of my favourite questions, http://www.experts-exchang­e.com/Q_23­787032.htm­l.

In short:
  • a list of numeric codes are contained in column A of the first worksheet, "Trial Balance"

  • other worksheets may use these codes within worksheet formulae (within formulae is an important distinction)

  • the Asker wanted to know which of these codes are not eventually referenced in the final worksheet, ""BS Movement"


Outlining an approach


This was an interesting problem from a coding viewpoint, the actual key coding blocks were easily definable, but the solution would require:
1) searching and parsing strings,
2) working with off sheet references (3D functionality in Excel is not always robust and cell precedents/dependents are no exception),
3) recursion

Attached is the sample file,


Main Subroutine


The first routine is a simple block of code that sets up the working range on the first sheet,  loops through and tests all non-empty calls for the referencing using the CellUsedinFormula routine, and then tidies up by removing the audit arrows and highlighting any non-referenced cells with a red background.

CellUsedinFormula Subroutine


This routine searches each worksheet other than "Trial Balance" for a partial string match for the numeric code items.

ExactMatch Function


When a match is found a regular expression user defined function (UDF), ExactMatch, is used to extract all numeric strings from the formula of interest. These strings are then subjected to a binary StrCompare to ensure that the match is exact, ie to prevent the false matching of "1000" when searching for "100".

A successful StrCompare means that we have identified a formula cell containing the numeric code from the "Trial Balance" sheet. If this cell resides in "BS Movement" then this code can be immediately ticked off the list, if not then a complete recursive check is required for all cells in the ActiveWorkbook that have a dependency on this cell formula. If any of this cells are located on the "BS Movement" sheet then we have a valid numeric code.

oneCellsDependents Function


The recursive dependency check is run using the oneCellsDependents UDF.

Cell precedents and dependents is one of those areas where Excel VBA is decidedly worksheet centric. The basic sample below shows that B3 in Sheet1 is dependant on both A1 of Sheet1 (same sheet) and A1 from Sheet2 (off sheet).
Formula.jpg
  • 13 KB
  • formula example
formula example
A simple test in the Immediate window of VBE shows that Excel sees only 1 DirectPrecedent (the same answer applies test for Precedent).
Immediate.jpg
  • 14 KB
  • Immediate window
Immediate window
So there is not a readymade collection of precedent (or in our particular example dependent) cells for us to apply a recursive search to.

Fortunately as per normal there is a workaround. Excel VBA provides us with the NavigateArrow Method, the VBA equivalent of the auditing toolbar found in 2003. This approach can be used to step though each of a cells dependents (or precedents) in turn by simulating clicking on the dependant arrow to each internal or off-sheet cell dependent.

Please note that the original credit for the navigate code that I modified below belongs to Bill Manville, Bill's original version  reference here was written to list all precedents of the active cell.

We now have the code blocks in place to identify cells of interest, and to test all the immediate dependencies of this cell to see if these cells are located on the "BS Movement"  worksheet. The remaining step is to broaden the dependency tracing code to handle a dependency chain of two or more cells. This is performed using a recursive programming technique, in plain English the oneCellsDependents calls itself until it either finds a dependent cell on the "BS Movement"  worksheet, or until the search has exhausted all the dependent cells without finding a match.
recursive.jpg
  • 56 KB
  • recursive code
recursive code


Other



Please note that I have used case-insensitive searching and comparison throughout this example so that the code could be easily modified for a text based search. See the code for the notes as to where to change this.


For more information on using Regular Expressions in VBA please see Patrick Matthew's excellent article,   Using Regular Expressions in Visual Basic.

I have made some minor modifications to the code from http://www.experts-exchang­e.com/Q_23­787032.htm­l  that is attached below.
 

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
If you liked this article and want to see more from this author, please click here.

If you found this article helpful, please click the Yes button near the:

      Was this article helpful?

label that is just below and to the right of this text.   Thanks!
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=  
Public Const strSourceSht = "Trial Balance"
Public Const strKeySht = "BS Movement"

Sub Main()
    Dim ws As Worksheet
    Dim rng1 As Range
    Dim rng2 As Range
    Dim i As Long
    Set ws = ThisWorkbook.Sheets(strSourceSht)
    Set rng1 = ws.Range(ws.[a2], ws.Cells(Rows.Count, "A").End(xlUp))
    If rng1 Is Nothing Then Exit Sub
    rng1.EntireColumn.Interior.ColorIndex = xlNone
    Application.ScreenUpdating = False
    For Each rng2 In rng1
        If Len(rng2.Value) > 0 Then
            rng2.NumberFormat = "@"
            If CellUsedinFormula(Trim(rng2.Value)) Then
            Else
                i = i + 1
                rng2.Interior.Color = vbRed
            End If
        End If
    Next
    For Each ws In ActiveWorkbook.Worksheets
        ws.ClearArrows
    Next ws
    Application.Goto rng1.Cells(1)
    Application.ScreenUpdating = True
    If i > 0 Then MsgBox i & " cells were found that are not used" & vbNewLine & "these have been coloured red"
End Sub

Function CellUsedinFormula(strFormula As String) As Boolean
    Dim ws As Worksheet
    Dim rng1 As Range
    Dim rng2 As Range
    Dim strTest As String
    Dim strFirst As String
    Dim bFound As Boolean

    'Look in each worksheet for the strings in the list from "Trial Balance"
    For Each ws In ThisWorkbook.Worksheets
        If ws.Name <> strSourceSht Then
            On Error Resume Next
            Set rng1 = ws.Cells.SpecialCells(xlCellTypeFormulas)
            On Error GoTo 0
            'Look in formula cells only
            If Not rng1 Is Nothing Then
                'the string searches and later string comparisons are case insenstive
                'so that the code can be easily modified to look for text rather than numeric strings
                Set rng2 = rng1.Find(strFormula, , xlFormulas, xlPart, xlByRows, , False)
                'For a case senstive search make the last argument TRUE, ie
                'Set rng2 = rng1.Find(strFormula, , xlFormulas, xlPart, xlByRows, , TRUE)

                'If the string is found, used a regular expression UDF to test for an exact match
                If Not rng2 Is Nothing Then
                    strTest = LCase$(strFormula)
                    bFound = ExactMatch(strTest, rng2)
                    If bFound Then Exit For
                    strFirst = rng2.Address
                    Do
                        Set rng2 = rng1.FindNext(rng2)
                        bFound = ExactMatch(strTest, rng2)
                        'If a valid reference is found then stop the search for the string
                        If bFound Then Exit For
                    Loop While Not rng2 Is Nothing And rng2.Address <> strFirst
                    Exit For
                End If
            End If
        End If
    Next
    CellUsedinFormula = bFound
End Function
Function ExactMatch(ByVal strTest, ByVal rng2) As Boolean
    Dim regex As Object
    Dim regM As Object
    Dim RegC As Object
    Dim bDependentRange As Boolean
    Set regex = CreateObject("vbscript.regexp")
    regex.Pattern = "\d+"
    regex.Global = True
    Set regM = regex.Execute(rng2.Formula)
    For Each RegC In regM
        If StrComp(LCase$(strTest), RegC, vbBinaryCompare) = 0 Then
            If rng2.Parent.Name = strKeySht Then
                ExactMatch = True
            Else
                'Call the recursive dependents function to test if this string
                'is eventually referred to on P&L Forecast
                bDependentRange = oneCellsDependents(rng2)
                If bDependentRange Then
                    'exit on a successful match
                    ExactMatch = True
                    Exit Function
                End If
            End If
        End If
    Next
End Function

Function oneCellsDependents(ByVal rng2) As Boolean
'initial code credited to Bill Manville
'brettdj adapted this code from a post by mike rickson
    Dim strAddress As String
    Dim rngReturn As Range
    Dim i As Long
    Dim lPreCount As Long
    Dim bFndTarget As Boolean

    'set the point to return to
    Set rngReturn = Selection
    strAddress = rng2.Parent.Name & "!" & rng2.Address

    ' use the NavigateArrow method to follow cell dependencies
    With rng2
        .ShowDependents
        .NavigateArrow dodependents, 1
        'recursive loop through dependents
        Do Until ActiveCell.Parent.Name & "!" & ActiveCell.Address = strAddress
            lPreCount = lPreCount + 1
            .NavigateArrow dodependents, lPreCount
            If ActiveCell.Parent.Name = strKeySht Then
                oneCellsDependents = True
                Exit Do
            Else
                'recursive function call below
                bFndTarget = oneCellsDependents(ActiveCell)
                'successful search. Leave function
                If bFndTarget = True Then
                    oneCellsDependents = True
                    GoTo LeaveMe
                End If
            End If
            'next dependent
            .NavigateArrow dodependents, lPreCount + 1
        Loop
        If oneCellsDependents Then GoTo LeaveMe
        'remove the auditing arrows
        ActiveCell.ShowDependents Remove:=True
    End With
    'Return selection to where it was
    With rngReturn
        .Parent.Activate
        .Select
    End With
LeaveMe:
End Function

                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:

Select allOpen in new window

    Asked On
    2010-03-28 at 19:06:26ID2762
    Tags
    Topic

    Microsoft Excel Spreadsheet Software

    Views
    1004

    Comments

    Add your Comment

    Please Sign up or Log in to comment on this article.

    Join Experts Exchange Today

    Gain Access to all our Tech Resources

    Get personalized answers

    Ask unlimited questions

    Access Proven Solutions

    Search 3.2 million solutions

    Read In-Depth How-To Guides

    1000+ articles, demos, & tips

    Watch Step by Step Tutorials

    Learn direct from top tech pros

    And Much More!

    Your complete tech resource

    See Plans and Pricing

    30-day free trial. Register in 60 seconds.

    Loading Advertisement...

    Top MS Excel Experts

    1. dlmille

      1,351,499

      Genius

      10,680 points yesterday

      Profile
      Rank: Genius
    2. ssaqibh

      542,555

      Sage

      0 points yesterday

      Profile
      Rank: Genius
    3. rorya

      381,757

      Wizard

      4,225 points yesterday

      Profile
      Rank: Savant
    4. imnorie

      334,112

      Wizard

      0 points yesterday

      Profile
      Rank: Genius
    5. teylyn

      282,850

      Guru

      20 points yesterday

      Profile
      Rank: Genius
    6. barryhoudini

      280,460

      Guru

      0 points yesterday

      Profile
      Rank: Genius
    7. redmondb

      235,511

      Guru

      2,000 points yesterday

      Profile
      Rank: Sage
    8. matthewspatrick

      230,947

      Guru

      2,010 points yesterday

      Profile
      Rank: Savant
    9. byundt

      197,840

      Guru

      820 points yesterday

      Profile
      Rank: Savant
    10. zorvek

      144,626

      Master

      0 points yesterday

      Profile
      Rank: Savant
    11. StephenJR

      136,537

      Master

      0 points yesterday

      Profile
      Rank: Genius
    12. nutsch

      117,005

      Master

      0 points yesterday

      Profile
      Rank: Genius
    13. gowflow

      110,036

      Master

      0 points yesterday

      Profile
      Rank: Sage
    14. MartinLiss

      107,333

      Master

      0 points yesterday

      Profile
      Rank: Wizard
    15. GlennLRay

      95,652

      Master

      0 points yesterday

      Profile
      Rank: Guru
    16. robhenson

      90,250

      Master

      0 points yesterday

      Profile
      Rank: Sage
    17. ScriptAddict

      88,470

      Master

      0 points yesterday

      Profile
      Rank: Guru
    18. kgerb

      85,022

      Master

      0 points yesterday

      Profile
      Rank: Wizard
    19. aikimark

      84,456

      Master

      3,310 points yesterday

      Profile
      Rank: Genius
    20. andrewssd3

      80,242

      Master

      0 points yesterday

      Profile
      Rank: Wizard
    21. Wiesje

      69,918

      Master

      0 points yesterday

      Profile
      Rank: Master
    22. Shanan212

      66,418

      Master

      0 points yesterday

      Profile
      Rank: Master
    23. krishnakrkc

      59,548

      Master

      0 points yesterday

      Profile
      Rank: Wizard
    24. Michael74

      54,744

      Master

      0 points yesterday

      Profile
      Rank: Wizard
    25. regmigrant

      51,070

      Master

      0 points yesterday

      Profile
      Rank: Guru

    Hall Of Fame