Excel VBA PDF Converted Text Extraction - vba

I am working with some code that was written for me, and I am having trouble with the Department value. I do not want it to only put out one word which is what it is doing now, and I do not know where to place it so that it doesn't remove all of the text except for just one word. This is the code:
(One of the lines will not space over to change it into a code view and I'm not sure how to fix that)
'===============================================================
Private Enum i_
ž__NONE = 0
ID
Last
First
Middle
Rank
Department
ž__
ž__FIRST = ž__NONE + 1
ž__LAST = ž__ - 1
End Enum
Public Sub EditedVersion_ExtractFieldValues()
Const l_Table_1 As String = "Table 1"
Const l_FieldValues As String = "FieldValues"
Const l_last_first_middle As String = "last first middle"
Const s_FieldNames As String = "id " & l_last_first_middle & " rank" & " department"
Const n_OutputRowsPerRecord As Long = 7
Dim ƒ As Excel.WorksheetFunction: Set ƒ = Excel.WorksheetFunction
Dim ¡ As Long
Dim dictFields As Object
Set dictFields = CreateObject("Scripting.Dictionary")
dictFields.CompareMode = vbTextCompare
With Worksheets
On Error Resume Next
.Add(After:=.Item(.count)).name = l_FieldValues
On Error GoTo 0
Application.DisplayAlerts = False
If .Item(.count).name <> l_FieldValues Then
.Item(.count).Delete
.Item(l_FieldValues).UsedRange.Clear
End If
.Item(l_FieldValues).Columns(1).NumberFormat = "#"
Application.DisplayAlerts = True
.Item(l_Table_1).Activate
End With
Dim astrFieldNames() As String
astrFieldNames = Split(" " & s_FieldNames, " ") ' Force index zero to a blank -> treat as base 1
With dictFields
.CompareMode = vbTextCompare
For ¡ = i_.ž__FIRST To i_.ž__LAST
dictFields.Add astrFieldNames(¡), ""
Next ¡
End With
Dim lngLastUsedRow As Long
lngLastUsedRow _
= Cells _
.find _
( _
What:="*" _
, After:=Cells(1) _
, LookIn:=xlFormulas _
, Lookat:=xlPart _
, SearchOrder:=xlByRows _
, SearchDirection:=xlPrevious _
) _
.Row
With Range(Rows(1), Rows(lngLastUsedRow))
Dim arngFoundCells(i_.ž__FIRST To i_.ž__LAST) As Range
For ¡ = i_.ž__FIRST To i_.ž__LAST
Set arngFoundCells(¡) = .find(What:=astrFieldNames(¡), After:=Cells(1))
Next ¡
Dim lngFirstFoundRow As Long
lngFirstFoundRow _
= ƒ.Min _
( _
arngFoundCells(i_.Last).Row _
, arngFoundCells(i_.First).Row _
, arngFoundCells(i_.Middle).Row _
)
Dim lngOuputSheetNextRow As Long
lngOuputSheetNextRow = 1
Dim varFoundCell As Variant
Dim lngNextFoundRow As Long
Dim rngNextFindStart As Range
Dim astrSplitValues() As String
Dim strFoundValue As String
Dim lngFieldCount As Long
Do
For ¡ = i_.ž__FIRST To i_.ž__LAST
'Debug.Print arngFoundCells(¡).Address; " ";
dictFields.Item(astrFieldNames(¡)) = ""
Next ¡
Debug.Print
Select Case True
Case arngFoundCells(i_.First).Row = arngFoundCells(i_.Middle).Row:
' Edge case: missing rank (found rank is for next employee) -> copy first to rank (simplifies following code)
'If varFoundCels(
If arngFoundCells(i_.Rank).Row <> arngFoundCells(i_.First).Row Then
Set arngFoundCells(i_.Rank) = arngFoundCells(i_.First)
End If
For Each varFoundCell In arngFoundCells
If Not varFoundCell Like astrFieldNames(i_.Department) Then
strFoundValue = ƒ.Trim(Replace(Replace(varFoundCell.Value2, vbLf, " "), ":", "")) & " "
If strFoundValue Like "[']*" Then strFoundValue = Mid$(strFoundValue, 2)
' ID field: only retain the first word of value
End If
If LCase$(strFoundValue) Like astrFieldNames(i_.ID) & "*" Then
strFoundValue = Left$(strFoundValue, InStr(InStr(strFoundValue, " ") + 1, strFoundValue, " "))
End If
' Department field: only retain the first word of value'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''THIS IS THE DEPARTMENT FIELD
If (varFoundCell) Like astrFieldNames(i_.Department) & " " Then
strFoundValue = Trim(varFoundCell.Value)
End If
' Edge case: no last name value in merged cell -> assume value is in first cell of following row
Debug.Print LCase$(strFoundValue)
If LCase$(strFoundValue) Like astrFieldNames(i_.Last) & " " Then
strFoundValue = ƒ.Trim(strFoundValue & Rows(varFoundCell.Row + 1).Cells(1).Value2) & " "
End If
' Edge case: Field names only in row -> assume field values are on the following row
If LCase$(strFoundValue) Like l_last_first_middle & "*" _
And Len(strFoundValue) - Len(Replace(strFoundValue, " ", "")) < 5 _
Then
strFoundValue = ƒ.Trim(strFoundValue & Rows(varFoundCell.Row + 1).Cells(1).Value2) & " "
End If
astrSplitValues = Split(" " & strFoundValue, " ") ' Force index zero to a blank -> treat as base 1
' Array contains one/some/all field names first and then the values (with a possible extra blank value)
lngFieldCount = Int(UBound(astrSplitValues) / 2)
For ¡ = 1 To lngFieldCount
dictFields.Item(astrSplitValues(¡)) = astrSplitValues(¡ + lngFieldCount)
Next ¡
Next varFoundCell
'Only allow the id to be on the previous row
If arngFoundCells(i_.ID).Row <> arngFoundCells(i_.First).Row - 1 Then
dictFields.Item(astrFieldNames(i_.ID)) = 0
End If
Case Else
Debug.Print " SKIPPED: ";
For ¡ = i_.ž__FIRST To i_.ž__LAST
'Debug.Print arngFoundCells(¡).Address; " ";
Next ¡
Debug.Print
For ¡ = i_.ž__FIRST To i_.ž__LAST
Debug.Print " "; ƒ.Trim(arngFoundCells(¡).Value2)
Next ¡
Debug.Print
End Select
Sheets(l_FieldValues).Columns(1).Cells(lngOuputSheetNextRow).Resize(n_OutputRowPerRecord - 1).Value = ƒ.Transpose(dictFields.Items)
lngOuputSheetNextRow = lngOuputSheetNextRow + n_OutputRowsPerRecord
Set rngNextFindStart = Rows(arngFoundCells(i_.First).Row + 2).Cells(1)
For ¡ = i_.ž__FIRST To i_.ž__LAST
Set arngFoundCells(¡) = .find(What:=astrFieldNames(¡),
After:=rngNextFindStart)
Next ¡
lngNextFoundRow _
= ƒ.Min _
( _
arngFoundCells(i_.Last).Row _
, arngFoundCells(i_.First).Row _
, arngFoundCells(i_.Middle).Row _
)
Loop While lngNextFoundRow <> lngFirstFoundRow
End With
End Sub

Related

Looking for Excel VBA solution to compare data

Need to compare data in two excel files by FacilityID column. Excel A data has to be aggregated first. In Excel B, FacilityID is unique row.
Tries VLOOKUP. It's getting too confusing. Need VBA solution
Function BrowseWin(mypath As String)
With Application.FileDialog(msoFileDialogOpen)
.InitialFileName = GetDir(mypath)
If .Show = -1 Then
BrowseWin = .SelectedItems.Item(1)
Else
BrowseWin = "-"
End If
End With
End Function
Function BrowseMac(mypath As String) As String
sMacScript = "set applescript's text item delimiters to "","" " & vbNewLine & _
"try " & vbNewLine & _
"set theFiles to (choose file " & _
"with prompt ""Please select a file or files"" default location alias """ & _
mypath & """ multiple selections allowed true) as string" & vbNewLine & _
"set applescript's text item delimiters to """" " & vbNewLine & _
"on error errStr number errorNumber" & vbNewLine & _
"return errorNumber " & vbNewLine & _
"end try " & vbNewLine & _
"return theFiles"
BrowseMac = MacScript(sMacScript)
End Function
Function grantFileAccess(filePermissionCandidates)
grantFileAccess = GrantAccessToMultipleFiles(filePermissionCandidates)
End Function
Public Function GetDir(File) As String
If Application.OperatingSystem Like "*Mac*" Then
div = ":"
Else
div = "\"
End If
x = InStrRev(File, div)
If x = 0 Then
GetDir = File
Else
GetDir = Left(File, x)
End If
End Function
Sub ChooseOptima_Click()
Dim startDir As String
startDir = GetDir("/")
If Application.OperatingSystem Like "*Mac*" Then
Path = BrowseMac(startDir)
If Path = "-43" Or Path = "-1700" Then
startDir = MacScript("return (path to documents folder) as String")
Path = BrowseMac(startDir)
End If
Else
Path = BrowseWin(startDir)
End If
If Left(Path, 1) <> "-" Then
Range("Optimafile") = Path
Range("C6").Select
End If
End Sub
Sub ChooseCRC_Click()
Dim startDir As String
startDir = GetDir("/")
If Application.OperatingSystem Like "*Mac*" Then
Path = BrowseMac(startDir)
If Path = "-43" Or Path = "-1700" Then
startDir = MacScript("return (path to documents folder) as String")
Path = BrowseMac(startDir)
End If
Else
Path = BrowseWin(startDir)
End If
If Left(Path, 1) <> "-" Then
Range("Crcfile") = Path
Range("A9").Select
End If
End Sub
Sub LoadReports()
Application.ScreenUpdating = False
With Worksheets("Sheet1")
ResultPath = Range("Optimafile").Value
grantAccessReq = Application.OperatingSystem Like "*Mac*"
If grantAccessReq Then
filePermissionCandidates = Array(NormPath, ExprPath)
grantFileAccess (filePermissionCandidates)
End If
CopyToReport (ResultPath)
End With
End Sub
Sub CopyToReport(OptimaPath As String)
Dim Sheet1, Sheet2 As Worksheet
Dim CrcPath, File1, File2 As String
File1 = Right$(OptimaPath, Len(OptimaPath) - InStrRev(OptimaPath, "\"))
CrcPath = Range("CrcFile").Value
File2 = Right$(CrcPath, Len(CrcPath) - InStrRev(CrcPath, "\"))
Application.DisplayAlerts = False
For Each ws In Worksheets
If ws.Name = "Optima Blue" Then
Sheets("Optima Blue").Delete
End If
Next
For Each ws In Worksheets
If ws.Name = "Optima Green" Then
Sheets("Optima Green").Delete
End If
Next
For Each ws In Worksheets
If ws.Name = "CRC Limit" Then
Sheets("CRC Limit").Delete
End If
Next
For Each ws In Worksheets
If ws.Name = "CRC Usage" Then
Sheets("CRC Usage").Delete
End If
Next
For Each ws In Worksheets
If ws.Name = "Result Limit" Then
Sheets("Result Limit").Delete
End If
Next
For Each ws In Worksheets
If ws.Name = "Result Usage" Then
Sheets("Result Usage").Delete
End If
Next
For Each ws In Worksheets
If ws.Name = "CRC Usage Consolidated" Then
Sheets("CRC Usage Consolidated").Delete
End If
Next
For Each ws In Worksheets
If ws.Name = "CRC Limit Consolidated" Then
Sheets("CRC Limit Consolidated").Delete
End If
Next
Application.DisplayAlerts = True
Workbooks.Open (OptimaPath)
Set Sheet1 = Workbooks(File1).Worksheets(1)
MakeOptimaBlueSheet
MakeOptimaGreenSheet
CloseBook (File1)
Workbooks.Open (CrcPath)
Set Sheet2 = Workbooks(File2).Worksheets("CRC")
MakeCRCSheetLimit
MakeCRCSheetUsage
CloseBook (File2)
MakeCRCSheetUsageConsolidated
MakeCRCSheetLimitConsolidated
MakeResultOptimaBlue
MakeResultOptimaGreen
MakeResultLimitReport
MakeResultUsageReport
End Sub
Sub MakeOptimaBlueSheet()
dlastRow = ActiveSheet.Cells(Rows.Count, "I").End(xlUp).Row
Dim rng, aRng, bRng As Range
Dim i, a, hSearch, head, headerCount As Integer
Set FirstCellExclude = ThisWorkbook.Sheets("Sheet1").Range("FirstCellExcludeHeader")
Set SecondCellExclude = ThisWorkbook.Sheets("Sheet1").Range("SecondCellExcludeHeader")
Set OptimaBlueHeaders = ThisWorkbook.Sheets("Sheet1").Range("OptimaBlueHeaders")
Set FindHeaders = ActiveSheet.Range("A1:ZZ1")
Set u = Union(FirstCellExclude, OptimaBlueHeaders, SecondCellExclude)
For Each fcell In FindHeaders
For Each oCell In u
If IsEmpty(fcell) Or IsEmpty(oCell) Then
ElseIf fcell = oCell Then
If Not IsEmpty(strRange) Then strRange = strRange + ","
Cell_Add = Split(fcell.Address, "$")
strRange = strRange + Cell_Add(1) + "1:" + Cell_Add(1) + CStr(dlastRow)
headerCount = headerCount + 1
Else
End If
Next oCell
Next fcell
ActiveSheet.Range(strRange).Select
Selection.Copy
With ThisWorkbook
Set ws = .Sheets.Add(After:=.Sheets(.Sheets.Count))
ws.Name = "Optima Blue"
ws.Paste
Set ws2 = ThisWorkbook.Sheets("Sheet1")
Set ExcludeRng = ws2.Range("FirstCellValues")
exclLastRow = ws2.Range("FirstCellValues").Rows.Count
Set searchFilterLetter = ws.Range("A1:Z1")
For Each gcell In searchFilterLetter
If IsEmpty(gcell) Then
ElseIf gcell = FirstCellExclude.Value Then
Cell_Add = Split(gcell.Address, "$")
FilterLetter = Cell_Add(1)
Debug.Print FilterLetter
Else
End If
Next gcell
For i = ws.Range(FilterLetter + "2:" & FilterLetter & dlastRow & " ").Cells.Count To 1 Step -1
For a = exclLastRow To 1 Step -1
If IsEmpty(ws.Cells(i, FilterLetter)) Or IsEmpty(ExcludeRng.Cells(a, 1)) Then
ElseIf ws.Cells(i, FilterLetter) = ExcludeRng.Cells(a, 1) Then
ws.Cells(i, FilterLetter).EntireRow.Delete
Else
End If
Next a
Next i
ws.Range(FilterLetter + "1:" & FilterLetter & dlastRow & " ").Delete
Set searchFilterLetter = ws.Range("A1:Z1")
For Each gcell In searchFilterLetter
If IsEmpty(gcell) Then
ElseIf gcell = SecondCellExclude.Value Then
Cell_Add = Split(gcell.Address, "$")
FilterLetter = Cell_Add(1)
Debug.Print FilterLetter
Else
End If
Next gcell
Set ExcludeRng2 = ws2.Range("SecondCellValues")
exclLastRow2 = ws2.Range("SecondCellValues").Rows.Count
For i = ws.Range(FilterLetter + "2:" & FilterLetter & dlastRow & " ").Cells.Count To 1 Step -1
For a = exclLastRow2 To 1 Step -1
If IsEmpty(ws.Cells(i, FilterLetter)) Or IsEmpty(ExcludeRng2.Cells(a, 1)) Then
ElseIf ws.Cells(i, FilterLetter) = ExcludeRng2.Cells(a, 1) Then
ws.Cells(i, FilterLetter).EntireRow.Delete
Else
End If
Next a
Next i
ws.Range(FilterLetter + "1:" & FilterLetter & dlastRow & " ").Delete
ws.Range("G1") = "Optima Blue Total"
ws.Range("G2").Formula = "=B2+C2+D2+E2+F2"
ws.Range("G2").Copy
ws.Range("G3:G" & dlastRow & " ").PasteSpecial (xlPasteAll)
ws.Range("G2:G" & dlastRow & " ").NumberFormat = "#,##0"
For Each rng In ws.Range("G2:G" & dlastRow & " ")
If rng.HasFormula Then
rng.Formula = rng.Value
End If
Next rng
ws.Range("B:F").Delete
End With
End Sub
Sub MakeOptimaGreenSheet()
dlastRow = ActiveSheet.Cells(Rows.Count, "I").End(xlUp).Row
Dim rng As Range
Dim i, a, hSearch, head, headerCount As Integer
Set FirstCellExclude = ThisWorkbook.Sheets("Sheet1").Range("FirstCellExcludeHeader")
Set SecondCellExclude = ThisWorkbook.Sheets("Sheet1").Range("SecondCellExcludeHeader")
Set OptimaGreenHeaders = ThisWorkbook.Sheets("Sheet1").Range("OptimaGreenHeaders")
Set FindHeaders = ActiveSheet.Range("A1:ZZ1")
Set u = Union(FirstCellExclude, SecondCellExclude, OptimaGreenHeaders)
For Each fcell In FindHeaders
For Each oCell In u
If IsEmpty(fcell) Or IsEmpty(oCell) Then
ElseIf fcell = oCell Then
If Not IsEmpty(strRange) Then strRange = strRange + ","
Cell_Add = Split(fcell.Address, "$")
strRange = strRange + Cell_Add(1) + "1:" + Cell_Add(1) + CStr(dlastRow)
headerCount = headerCount + 1
Else
End If
Next oCell
Next fcell
ActiveSheet.Range(strRange).Select
Selection.Copy
With ThisWorkbook
Set ws = .Sheets.Add(After:=.Sheets(.Sheets.Count))
ws.Name = "Optima Green"
ws.Paste
Set ws2 = ThisWorkbook.Sheets("Sheet1")
Set ExcludeRng = ws2.Range("FirstCellValues")
exclLastRow = ws2.Range("FirstCellValues").Rows.Count
Set searchFilterLetter = ws.Range("A1:Z1")
For Each gcell In searchFilterLetter
If IsEmpty(gcell) Then
ElseIf gcell = FirstCellExclude.Value Then
Cell_Add = Split(gcell.Address, "$")
FilterLetter = Cell_Add(1)
Debug.Print FilterLetter
Else
End If
Next gcell
For i = ws.Range(FilterLetter + "2:" & FilterLetter & dlastRow & " ").Cells.Count To 1 Step -1
For a = exclLastRow To 1 Step -1
If IsEmpty(ws.Cells(i, FilterLetter)) Or IsEmpty(ExcludeRng.Cells(a, 1)) Then
ElseIf ws.Cells(i, FilterLetter) = ExcludeRng.Cells(a, 1) Then
ws.Cells(i, FilterLetter).EntireRow.Delete
Else
End If
Next a
Next i
ws.Range(FilterLetter + "1:" & FilterLetter & dlastRow & " ").Delete
Set searchFilterLetter = ws.Range("A1:Z1")
For Each gcell In searchFilterLetter
If IsEmpty(gcell) Then
ElseIf gcell = SecondCellExclude.Value Then
Cell_Add = Split(gcell.Address, "$")
FilterLetter = Cell_Add(1)
Debug.Print FilterLetter
Else
End If
Next gcell
Set ExcludeRng2 = ws2.Range("SecondCellValues")
exclLastRow2 = ws2.Range("SecondCellValues").Rows.Count
For i = ws.Range(FilterLetter + "2:" & FilterLetter & dlastRow & " ").Cells.Count To 1 Step -1
For a = exclLastRow2 To 1 Step -1
If IsEmpty(ws.Cells(i, FilterLetter)) Or IsEmpty(ExcludeRng2.Cells(a, 1)) Then
ElseIf ws.Cells(i, FilterLetter) = ExcludeRng2.Cells(a, 1) Then
ws.Cells(i, FilterLetter).EntireRow.Delete
Else
End If
Next a
Next i
ws.Range(FilterLetter + "1:" & FilterLetter & dlastRow & " ").Delete
ws.Range("G1") = "Optima Green Total"
ws.Range("G2").Formula = "=B2+C2+D2+E2+F2"
ws.Range("G2").Copy
ws.Range("G3:G" & dlastRow & " ").PasteSpecial (xlPasteAll)
ws.Range("G2:G" & dlastRow & " ").NumberFormat = "#,##0"
For Each rng In ws.Range("G2:G" & dlastRow & " ")
If rng.HasFormula Then
rng.Formula = rng.Value
End If
Next rng
ws.Range("B:F").Delete
End With
End Sub
Sub MakeCRCSheetLimit()
dlastRow = ActiveSheet.Cells(Rows.Count, "D").End(xlUp).Row
Dim rng As Range
Dim i, a, hSearch, head, headerCount As Integer
Set FirstCrcFilter = ThisWorkbook.Sheets("Sheet1").Range("FirstCrcFilterHeader")
Set SecondCrcFilter = ThisWorkbook.Sheets("Sheet1").Range("SecondCrcFilterHeader")
Set CrcLimitHeaders = ThisWorkbook.Sheets("Sheet1").Range("CrcLimitHeaders")
Set FindHeaders = ActiveSheet.Range("A1:ZZ1")
Set u = Union(CrcLimitHeaders, FirstCrcFilter, SecondCrcFilter)
For Each fcell In FindHeaders
For Each oCell In u
If IsEmpty(fcell) Or IsEmpty(oCell) Then
ElseIf fcell = oCell Then
If Not IsEmpty(strRange) Then strRange = strRange + ","
Cell_Add = Split(fcell.Address, "$")
strRange = strRange + Cell_Add(1) + "1:" + Cell_Add(1) + CStr(dlastRow)
headerCount = headerCount + 1
Else
End If
Next oCell
Next fcell
ActiveSheet.Range(strRange).Select
Selection.Copy
With ThisWorkbook
Set ws = .Sheets.Add(After:=.Sheets(.Sheets.Count))
ws.Name = "CRC Limit"
ws.Paste
Set ws2 = ThisWorkbook.Sheets("Sheet1")
Set ExcludeRng = ws2.Range("FirstCrcFilterValues")
exclLastRow = ws2.Range("FirstCrcFilterValues").Rows.Count
Set searchFilterLetter = ws.Range("A1:Zz1")
For Each gcell In searchFilterLetter
If IsEmpty(gcell) Then
ElseIf gcell = FirstCrcFilter Then
Cell_Add = Split(gcell.Address, "$")
FilterLetter = Cell_Add(1)
Else
End If
Next gcell
For i = ws.Range(FilterLetter + "2:" & FilterLetter & dlastRow & " ").Cells.Count To 1 Step -1
For a = exclLastRow To 1 Step -1
If IsEmpty(ws.Cells(i, FilterLetter)) Or IsEmpty(ExcludeRng.Cells(a, 1)) Then
ElseIf ws.Cells(i, FilterLetter) = ExcludeRng.Cells(a, 1) Then
ws.Cells(i, FilterLetter).EntireRow.Delete
Else
End If
Next a
Next i
ws.Range(FilterLetter + "1:" & FilterLetter & dlastRow & " ").Delete
Set ExcludeRng2 = ws2.Range("SecondCrcFilterValues")
exclLastRow2 = ExcludeRng2.Rows.Count
Set searchFilterLetter = ws.Range("A1:Zz1")
For Each gcell In searchFilterLetter
If IsEmpty(gcell) Then
ElseIf gcell = SecondCrcFilter Then
Cell_Add = Split(gcell.Address, "$")
FilterLetter = Cell_Add(1)
Else
End If
Next gcell
For i = ws.Range(FilterLetter + "2:" & FilterLetter & dlastRow & " ").Cells.Count To 1 Step -1
For a = exclLastRow2 To 1 Step -1
Debug.Print ws.Cells(i, FilterLetter), ExcludeRng2.Cells(a, 1)
If IsEmpty(ws.Cells(i, FilterLetter)) Or IsEmpty(ExcludeRng2.Cells(a, 1)) Then
ElseIf ws.Cells(i, FilterLetter) = ExcludeRng2.Cells(a, 1) Then
ws.Cells(i, FilterLetter).EntireRow.Delete
Else
End If
Next a
Next i
ws.Range(FilterLetter + "1:" & FilterLetter & dlastRow & " ").Delete
ws.Range("B2:B" & dlastRow & " ").NumberFormat = "#,##0"
End With
End Sub
Sub MakeCRCSheetUsage()
dlastRow = ActiveSheet.Cells(Rows.Count, "D").End(xlUp).Row
Dim rng As Range
Dim i, a, hSearch, head, headerCount As Integer
Set FirstCrcFilter = ThisWorkbook.Sheets("Sheet1").Range("FirstCrcFilterHeader")
Set SecondCrcFilter = ThisWorkbook.Sheets("Sheet1").Range("SecondCrcFilterHeader")
Set CrcLimitHeaders = ThisWorkbook.Sheets("Sheet1").Range("CrcUsageHeaders")
Set FindHeaders = ActiveSheet.Range("A1:ZZ1")
Set u = Union(CrcLimitHeaders, FirstCrcFilter, SecondCrcFilter)
For Each fcell In FindHeaders
For Each oCell In u
If IsEmpty(fcell) Or IsEmpty(oCell) Then
ElseIf fcell = oCell Then
If Not IsEmpty(strRange) Then strRange = strRange + ","
Cell_Add = Split(fcell.Address, "$")
strRange = strRange + Cell_Add(1) + "1:" + Cell_Add(1) + CStr(dlastRow)
headerCount = headerCount + 1
Else
End If
Next oCell
Next fcell
ActiveSheet.Range(strRange).Select
Selection.Copy
With ThisWorkbook
Set ws = .Sheets.Add(After:=.Sheets(.Sheets.Count))
ws.Name = "CRC Usage"
ws.Paste
Set ws2 = ThisWorkbook.Sheets("Sheet1")
Set ExcludeRng = ws2.Range("FirstCrcFilterValues")
exclLastRow = ws2.Range("FirstCrcFilterValues").Rows.Count
Set searchFilterLetter = ws.Range("A1:Zz1")
For Each gcell In searchFilterLetter
If IsEmpty(gcell) Then
ElseIf gcell = FirstCrcFilter Then
Cell_Add = Split(gcell.Address, "$")
FilterLetter = Cell_Add(1)
Else
End If
Next gcell
For i = ws.Range(FilterLetter + "2:" & FilterLetter & dlastRow & " ").Cells.Count To 1 Step -1
For a = exclLastRow To 1 Step -1
If IsEmpty(ws.Cells(i, FilterLetter)) Or IsEmpty(ExcludeRng.Cells(a, 1)) Then
ElseIf ws.Cells(i, FilterLetter) = ExcludeRng.Cells(a, 1) Then
ws.Cells(i, FilterLetter).EntireRow.Delete
Else
End If
Next a
Next i
ws.Range(FilterLetter + "1:" & FilterLetter & dlastRow & " ").Delete
Set ExcludeRng2 = ws2.Range("SecondCrcFilterValues")
exclLastRow2 = ExcludeRng2.Rows.Count
Set searchFilterLetter = ws.Range("A1:Zz1")
For Each gcell In searchFilterLetter
If IsEmpty(gcell) Then
ElseIf gcell = SecondCrcFilter Then
Cell_Add = Split(gcell.Address, "$")
FilterLetter = Cell_Add(1)
Else
End If
Next gcell
For i = ws.Range(FilterLetter + "2:" & FilterLetter & dlastRow & " ").Cells.Count To 1 Step -1
For a = exclLastRow2 To 1 Step -1
If IsEmpty(ws.Cells(i, FilterLetter)) Or IsEmpty(ExcludeRng2.Cells(a, 1)) Then
ElseIf ws.Cells(i, FilterLetter) = ExcludeRng2.Cells(a, 1) Then
ws.Cells(i, FilterLetter).EntireRow.Delete
Else
End If
Next a
Next i
ws.Range(FilterLetter + "1:" & FilterLetter & dlastRow & " ").Delete
ws.Range("B2:B" & dlastRow & " ").NumberFormat = "#,##0"
End With
End Sub
Sub MakeCRCSheetUsageConsolidated()
Dim CRCSheetUsageConsolidated As Worksheet, Result As Worksheet
Dim ConsolidateRangeArray As Variant
Set CRCSheetUsageConsolidated = Sheets("CRC Usage")
CRCSheetUsageConsolidated.Select
dlastRow = CRCSheetUsageConsolidated.Cells(Rows.Count, "A").End(xlUp).Row
CRCSheetUsageConsolidated.Select
CRCSheetUsageConsolidated.Range("A1:A" & dlastRow & " ").Select
Selection.Copy
With ThisWorkbook
Set ws = .Sheets.Add(After:=.Sheets(.Sheets.Count))
ws.Name = "CRC Usage Consolidated"
Selection.Consolidate Sources:= _
"'CRC Usage'!R1C1:R" & dlastRow & "C2", Function:= _
xlSum, TopRow:=True, LeftColumn:=True, CreateLinks:=False
End With
End Sub
Sub MakeCRCSheetLimitConsolidated()
Dim CRCSheetLimitConsolidated As Worksheet, Result As Worksheet
Dim ConsolidateRangeArray As Variant
Set CRCSheetLimitConsolidated = Sheets("CRC Limit")
CRCSheetLimitConsolidated.Select
dlastRow = CRCSheetLimitConsolidated.Cells(Rows.Count, "A").End(xlUp).Row
CRCSheetLimitConsolidated.Select
CRCSheetLimitConsolidated.Range("A1:A" & dlastRow & " ").Select
Selection.Copy
With ThisWorkbook
Set ws = .Sheets.Add(After:=.Sheets(.Sheets.Count))
ws.Name = "CRC Limit Consolidated"
Selection.Consolidate Sources:= _
"'CRC Limit'!R1C1:R" & dlastRow & "C2", Function:= _
xlSum, TopRow:=True, LeftColumn:=True, CreateLinks:=False
End With
End Sub
Sub MakeResultOptimaBlue()
Dim OptimaBlue As Worksheet, Result As Worksheet
Dim ConsolidateRangeArray As Variant
Set OptimaBlue = Sheets("Optima Blue")
OptimaBlue.Select
dlastRow = OptimaBlue.Cells(Rows.Count, "A").End(xlUp).Row
OptimaBlue.Range("A1:A" & dlastRow & " ").Select
Selection.Copy
With ThisWorkbook
Set ws = .Sheets.Add(After:=.Sheets(.Sheets.Count))
ws.Name = "Result Limit"
Selection.Consolidate Sources:= _
"'Optima Blue'!R1C1:R" & dlastRow & "C2", Function:= _
xlSum, TopRow:=True, LeftColumn:=True, CreateLinks:=False
End With
End Sub
Sub MakeResultOptimaGreen()
Dim OptimaGreen As Worksheet, Result As Worksheet
Dim ConsolidateRangeArray As Variant
Set OptimaGreen = Sheets("Optima Green")
OptimaGreen.Select
dlastRow = OptimaGreen.Cells(Rows.Count, "A").End(xlUp).Row
OptimaGreen.Range("A1:A" & dlastRow & " ").Select
Selection.Copy
With ThisWorkbook
Set ws = .Sheets.Add(After:=.Sheets(.Sheets.Count))
ws.Name = "Result Usage"
Selection.Consolidate Sources:= _
"'Optima Green'!R1C1:R" & dlastRow & "C2", Function:= _
xlSum, TopRow:=True, LeftColumn:=True, CreateLinks:=False
End With
End Sub
Sub MakeResultLimitReport()
Dim shOptima As Worksheet, shCrc As Worksheet, shResult As Worksheet
Dim aRng, bRng As Range
Dim Fnd As Range
Dim ListObject As ListObject
Set shOptima = Sheets("Optima Blue")
Set shCrc = Sheets("CRC Limit Consolidated")
Set shResult = Sheets("Result Limit")
bLastRow = shResult.Cells(Rows.Count, "A").End(xlUp).Row
aLastRow = shCrc.Cells(Rows.Count, "A").End(xlUp).Row
Set aRng = shCrc.Range("A2:A" & aLastRow & "") '127
Set bRng = shResult.Range("A2:A" & bLastRow & "") '71
For Each aCell In aRng
For Each bcell In bRng
If aCell Is Nothing Or bcell Is Nothing Then
ElseIf aCell.Text = bcell.Text Then
bcell.Offset(0, 2).Formula = "='CRC Limit Consolidated'!" & aCell.Offset(0, 1).Address & " "
Else
End If
Next bcell
Next aCell
shResult.Range("D2").Formula = "=IF(B2=0,IF(C2=0,0,abs(((B2-C2)/C2)*100)),abs(((C2-B2)/B2)*100))"
shResult.Range("D2").Copy
shResult.Range("D3:D" & bLastRow).PasteSpecial (xlPasteAll)
shResult.Range("D2:D" & bLastRow).NumberFormat = "#,##0"
shResult.Range("D2:D" & bLastRow).NumberFormat = "#,##0.00"
shResult.Activate
shResult.Range("A1") = "Facility ID"
shResult.Range("D1") = "Diff in percent"
shResult.Range("C1") = "CRC Limit"
shResult.Range("B2:D" & bLastRow & " ").NumberFormat = "#,##0"
shOptima.Range("E1:g1").Interior.ColorIndex = 35
Range("A1:G" & bLastRow).EntireColumn.AutoFit
Range("A2:D" & bLastRow).Sort key1:=Range("D2:D" & bLastRow), order1:=xlDescending, Header:=xlNo
Set ListObject = ActiveSheet.ListObjects.Add(xlSrcRange, Range("A1").CurrentRegion, , xlYes)
With ListObject
.Name = "Table4"
.TableStyle = "TableStyleMedium5"
End With
End Sub
Sub MakeResultUsageReport()
Dim shOptima As Worksheet, shCrc As Worksheet, shResult As Worksheet
Dim aRng, bRng As Range
Dim Fnd As Range
Dim ListObject As ListObject
Set shOptima = Sheets("Optima Green")
Set shCrc = Sheets("CRC Usage Consolidated")
Set shResult = Sheets("Result Usage")
bLastRow = shResult.Cells(Rows.Count, "A").End(xlUp).Row
aLastRow = shCrc.Cells(Rows.Count, "A").End(xlUp).Row
Set aRng = shCrc.Range("A2:A" & aLastRow & "")
Set bRng = shResult.Range("A2:A" & bLastRow & "")
For Each aCell In aRng
For Each bcell In bRng
If aCell Is Nothing Or bcell Is Nothing Then
ElseIf aCell.Text = bcell.Text Then
bcell.Offset(0, 2).Formula = "='CRC Usage Consolidated'!" & aCell.Offset(0, 1).Address & " "
Else
End If
Next bcell
Next aCell
shResult.Range("D2").Formula = "=IF(B2=0,IF(C2=0,0,abs(((B2-C2)/C2)*100)),abs(((C2-B2)/B2)*100))"
shResult.Range("D2").Copy
shResult.Range("D3:D" & bLastRow).PasteSpecial (xlPasteAll)
shResult.Range("D2:D" & bLastRow).NumberFormat = "#,##0"
shResult.Range("D2:D" & bLastRow).NumberFormat = "#,##0.00"
shResult.Activate
shResult.Range("A1") = "Facility ID"
shResult.Range("D1") = "Diff in percent"
shResult.Range("C1") = "CRC Usage"
shResult.Range("B2:D" & bLastRow & " ").NumberFormat = "#,##0"
shOptima.Range("E1:g1").Interior.ColorIndex = 35
Range("A1:G" & bLastRow).EntireColumn.AutoFit
Range("A2:D" & bLastRow).Sort key1:=Range("D2:D" & bLastRow), order1:=xlDescending, Header:=xlNo
Set ListObject = ActiveSheet.ListObjects.Add(xlSrcRange, Range("A1").CurrentRegion, , xlYes)
With ListObject
.Name = "Table4"
.TableStyle = "TableStyleMedium4"
End With
Application.ScreenUpdating = True
End Sub
Sub CloseBook(File As String)
Workbooks(File).Close savechanges:=False
End Sub

Excel VBA code to trace precedents of cell

I have the following code which traces the precedents of an active cell and spits out a message box with the info. (It also searches for precedents in other worksheets and workbooks).
I am new to VBA, and I would like to request help on changing this code to spit out the precedent cell, formula, and address into a new worksheet after the active worksheet. Please can someone help me understand how to do this.
Should I create a new function to create a new sheet and copy the dynamic info onto it within the first sub?
For example, if I have the formula A1 + B1 in cell C1 of Sheet1, then I want a row in Sheet2 (newly created sheet) which shows Target Cell as C1, Target Sheet as Sheet1, Source Cell as A1, and Source Sheet as Sheet1. I also want another row in Sheet2 which shows Target Cell as C1, Target Sheet as Sheet1, Source Cell as B1, and Source Sheet as Sheet1.
Sheet2:
Code:
Option Explicit
Public OtherWbRefs As Collection
Public ClosedWbRefs As Collection
Public SameWbOtherSheetRefs As Collection
Public SameWbSameSheetRefs As Collection
Public CountOfClosedWb As Long
Dim headerString As String
Sub RunMe()
Call FindCellPrecedents(ActiveCell)
End Sub
Sub FindCellPrecedents(homeCell As Range)
Dim i As Long, j As Long, pointer As Long
Dim maxReferences As Long
Dim outStr As String
Dim userInput As Long
If homeCell.HasFormula Then
Set OtherWbRefs = New Collection: CountOfClosedWb = 0
Set SameWbOtherSheetRefs = New Collection
Set SameWbSameSheetRefs = New Collection
Rem find closed precedents from formula String
Call FindClosedWbReferences(homeCell)
Rem find Open precedents from navigate arrows
homeCell.Parent.ClearArrows
homeCell.ShowPrecedents
headerString = "in re: the formula in " & homeCell.Address(, , , True)
maxReferences = Int(Len(homeCell.Formula) / 3) + 1
On Error GoTo LoopOut:
For j = 1 To maxReferences
homeCell.NavigateArrow True, 1, j
If ActiveCell.Address(, , , True) = homeCell.Address(, , , True) Then
Rem closedRef
Call CategorizeReference("<ClosedBook>", homeCell)
Else
Call CategorizeReference(ActiveCell, homeCell)
End If
Next j
LoopOut:
On Error GoTo 0
For j = 2 To maxReferences
homeCell.NavigateArrow True, j, 1
If ActiveCell.Address(, , , True) = homeCell.Address(, , , True) Then Exit For
Call CategorizeReference(ActiveCell, homeCell)
Next j
homeCell.Parent.ClearArrows
Rem integrate ClosedWbRefs (from parsing) With OtherWbRefs (from navigation)
If ClosedWbRefs.Count <> CountOfClosedWb Then
If ClosedWbRefs.Count = 0 Then
MsgBox homeCell.Address(, , , True) & " contains a formula with no precedents."
Exit Sub
Else
MsgBox "string-" & ClosedWbRefs.Count & ":nav " & CountOfClosedWb
MsgBox "Methods find different # of closed precedents."
End
End If
End If
pointer = 1
For j = 1 To OtherWbRefs.Count
If OtherWbRefs(j) Like "<*" Then
OtherWbRefs.Add Item:=ClosedWbRefs(pointer), key:="closed" & CStr(pointer), after:=j
pointer = pointer + 1
OtherWbRefs.Remove j
End If
Next j
Rem present findings
outStr = homeCell.Address(, , , True) & " contains a formula with:"
outStr = outStr & vbCrLf & vbCrLf & CountOfClosedWb & " precedents in closed workbooks."
outStr = outStr & vbCr & (OtherWbRefs.Count - CountOfClosedWb) & " precedents in other workbooks that are open."
outStr = outStr & vbCr & SameWbOtherSheetRefs.Count & " precedents on other sheets in the same workbook."
outStr = outStr & vbCr & SameWbSameSheetRefs.Count & " precedents on the same sheet."
outStr = outStr & vbCrLf & vbCrLf & "YES - See details about Other Books."
outStr = outStr & vbCr & "NO - See details about The Active Book."
Do
userInput = MsgBox(prompt:=outStr, Title:=headerString, Buttons:=vbYesNoCancel + vbDefaultButton3)
Select Case userInput
Case Is = vbYes
MsgBox prompt:=OtherWbDetail(), Title:=headerString, Buttons:=vbOKOnly
Case Is = vbNo
MsgBox prompt:=SameWbDetail(), Title:=headerString, Buttons:=vbOKOnly
End Select
Loop Until userInput = vbCancel
Else
MsgBox homeCell.Address(, , , True) & vbCr & " does not contain a formula."
End If
End Sub
Sub CategorizeReference(Reference As Variant, Home As Range)
Rem assigns reference To the appropriate collection
If TypeName(Reference) = "String" Then
Rem String indicates reference To closed Wb
OtherWbRefs.Add Item:=Reference, key:=CStr(OtherWbRefs.Count)
CountOfClosedWb = CountOfClosedWb + 1
Else
If Home.Address(, , , True) = Reference.Address(, , , True) Then Exit Sub
If Home.Parent.Parent.Name = Reference.Parent.Parent.Name Then
Rem reference In same Wb
If Home.Parent.Name = Reference.Parent.Name Then
Rem sameWb sameSheet
SameWbSameSheetRefs.Add Item:=Reference.Address(, , , True), key:=CStr(SameWbSameSheetRefs.Count)
Else
Rem sameWb Other sheet
SameWbOtherSheetRefs.Add Item:=Reference.Address(, , , True), key:=CStr(SameWbOtherSheetRefs.Count)
End If
Else
Rem reference To other Open Wb
OtherWbRefs.Add Item:=Reference.Address(, , , True), key:=CStr(OtherWbRefs.Count)
End If
End If
End Sub
Sub FindClosedWbReferences(inRange As Range)
Rem fills the collection With closed precedents parsed from the formula String
Dim testString As String, returnStr As String, remnantStr As String
testString = inRange.Formula
Set ClosedWbRefs = New Collection
Do
returnStr = NextClosedWbRefStr(testString, remnantStr)
ClosedWbRefs.Add Item:=returnStr, key:=CStr(ClosedWbRefs.Count)
testString = remnantStr
Loop Until returnStr = vbNullString
ClosedWbRefs.Remove ClosedWbRefs.Count
End Sub
Function NextClosedWbRefStr(FormulaString As String, Optional ByRef Remnant As String) As String
Dim workStr As String
Dim start As Long, interval As Long, del As Long
For start = 1 To Len(FormulaString)
For interval = 2 To Len(FormulaString) - start + 1
workStr = Mid(FormulaString, start, interval)
If workStr Like Chr(39) & "[!!]*'![$A-Z]*#" Then
If workStr Like Chr(39) & "[!!]*'!*[$1-9A-Z]#" Then
interval = interval - CLng(Mid(FormulaString, start + interval, 1) Like "#")
interval = interval - 3 * CLng(Mid(FormulaString, start + interval, 1) = ":")
interval = interval - CLng(Mid(FormulaString, start + interval, 1) Like "[$1-9A-Z]")
interval = interval - CLng(Mid(FormulaString, start + interval, 1) Like "[$1-9A-Z]")
interval = interval - CLng(Mid(FormulaString, start + interval, 1) Like "[$1-9A-Z]")
interval = interval - CLng(Mid(FormulaString, start + interval, 1) Like "[$1-9A-Z]")
NextClosedWbRefStr = Mid(FormulaString, start, interval)
Remnant = Mid(FormulaString, start + interval)
Exit Function
End If
End If
Next interval
Next start
End Function
Function OtherWbDetail() As String
Rem display routine
OtherWbDetail = OtherWbDetail & "There are " & OtherWbRefs.Count & " references to other workbooks. "
OtherWbDetail = OtherWbDetail & IIf(CBool(CountOfClosedWb), CountOfClosedWb & " are closed.", vbNullString)
OtherWbDetail = OtherWbDetail & vbCr & "They appear in the formula in this order:" & vbCrLf & vbCrLf
OtherWbDetail = OtherWbDetail & rrayStr(OtherWbRefs, vbCr)
End Function
Function SameWbDetail() As String
Rem display routine
SameWbDetail = SameWbDetail & "There are " & SameWbOtherSheetRefs.Count & " ref.s to other sheets in the same book."
SameWbDetail = SameWbDetail & vbCr & "They appear in this order, including duplications:" & vbCrLf & vbCrLf
SameWbDetail = SameWbDetail & rrayStr(SameWbOtherSheetRefs, vbCr)
SameWbDetail = SameWbDetail & vbCrLf & vbCrLf & "There are " & SameWbSameSheetRefs.Count & " precedents on the same sheet."
SameWbDetail = SameWbDetail & vbCr & "They are (out of order, duplicates not noted):" & vbCrLf & vbCrLf
SameWbDetail = SameWbDetail & rrayStr(SameWbSameSheetRefs, vbCr)
End Function
Function rrayStr(ByVal inputRRay As Variant, Optional Delimiter As String)
Rem display routine
Dim xVal As Variant
If IsEmpty(inputRRay) Then Exit Function
If Delimiter = vbNullString Then Delimiter = " "
For Each xVal In inputRRay
rrayStr = rrayStr & Delimiter & xVal
Next xVal
rrayStr = Mid(rrayStr, Len(Delimiter) + 1)
End Function
I believe it is best to add two new functions:
add the "info sheet" (and store it in a variable for later use)
Sub addInfoSheet()
Dim oldSheet
Set oldSheet = ActiveSheet
Sheets.Add After:=ActiveSheet
Set infoSheet = Sheets(ActiveSheet.Index)
oldSheet.Select
End Sub
a sub that stores one line to the sheet, something like:
Sub addRowToInfoSheet(targetSheet As String, targetRange As String, sourceSheet As String, sourceRange As String)
infoSheet.Cells(rowInInfoSheet, 1) = targetSheet
infoSheet.Cells(rowInInfoSheet, 2) = targetRange
infoSheet.Cells(rowInInfoSheet, 3) = sourceSheet
infoSheet.Cells(rowInInfoSheet, 4) = sourceRange
rowInInfoSheet = rowInInfoSheet + 1
End Sub
Let me know if this helps.
EDIT: (v0.2) Now works for all sheets in current workbook. (And fleshed out for other workbooks.)
You could do something sneaky and hook the MsgBox function and parse the data from its output.
Just do a global search for MsgBox in your code and replace it with, for example, MsgBoxInterceptor.
Then you write the MsgBoxInterceptor() function, oh, say like the one below ;)
Run the RunMe() sub like normal, and voila! Instead of output to the screen, you get output to a new worksheet.
No need to even work out what your original code is doing!
NB The function provided only pulls the precedents from the active workbook.
'v0.2
Private Function MsgBoxInterceptor _
( _
Prompt, _
Optional Buttons As VbMsgBoxStyle = vbOKOnly, _
Optional Title, _
Optional HelpFile, _
Optional Context _
) _
As VBA.VbMsgBoxResult
Const i_TargetCell As Long = 1
Const i_TargetSheet As Long = 2
Const i_SourceCell As Long = 3
Const i_SourceSheet As Long = 4
Static slngState As Long
Static srngDataRow As Range
Static sstrTargetCell As String
Static sstrTargetSheet As String
Static slngClosedBookCount As Long
Static slngOpenBookCount As Long
Static slngSameBookCount As Long
Static slngSameSheetCount As Long
Dim f As WorksheetFunction: Set f = WorksheetFunction
Dim lngBegin As Long
Dim lngEnd As Long
Dim i As Long
Select Case slngState
Case 0: ' Get counts and target
Worksheets.Add After:=ActiveSheet
Set srngDataRow = ActiveSheet.Range("A1:D1")
srngDataRow.Value = Split("Target Cell:Target Sheet:Source Cell:Source Sheet", ":")
Set srngDataRow = srngDataRow.Offset(1)
lngBegin = InStr(1, Prompt, "]") + 1
lngEnd = InStr(lngBegin, Prompt, "'")
sstrTargetSheet = Mid$(Prompt, lngBegin, lngEnd - lngBegin)
srngDataRow.Cells(i_TargetSheet) = sstrTargetSheet
lngBegin = InStr(lngEnd, Prompt, "$") + 1
lngEnd = InStr(lngBegin, Prompt, " ")
sstrTargetCell = f.Substitute(Mid$(Prompt, lngBegin, lngEnd - lngBegin), "$", "")
srngDataRow.Cells(i_TargetCell) = sstrTargetCell
lngBegin = InStr(lngEnd, Prompt, ":") + 3
lngEnd = InStr(lngBegin, Prompt, " ")
slngClosedBookCount = Val(Mid$(Prompt, lngBegin, lngEnd - lngBegin))
lngBegin = InStr(lngEnd, Prompt, ".") + 2
lngEnd = InStr(lngBegin, Prompt, " ")
slngOpenBookCount = Val(Mid$(Prompt, lngBegin, lngEnd - lngBegin))
lngBegin = InStr(lngEnd, Prompt, ".") + 2
lngEnd = InStr(lngBegin, Prompt, " ")
slngSameBookCount = Val(Mid$(Prompt, lngBegin, lngEnd - lngBegin))
lngBegin = InStr(lngEnd, Prompt, ".") + 2
lngEnd = InStr(lngBegin, Prompt, " ")
slngSameSheetCount = Val(Mid$(Prompt, lngBegin, lngEnd - lngBegin))
slngState = slngState + 1
MsgBoxInterceptor = vbNo
Case 1: ' Get same book sources
lngEnd = InStr(1, Prompt, "[")
For i = 1 To slngSameBookCount
srngDataRow.Cells(i_TargetCell) = sstrTargetCell
srngDataRow.Cells(i_TargetSheet) = sstrTargetSheet
lngBegin = InStr(lngEnd, Prompt, "]") + 1
lngEnd = InStr(lngBegin, Prompt, "'")
srngDataRow.Cells(i_SourceSheet) = Mid$(Prompt, lngBegin, lngEnd - lngBegin)
lngBegin = InStr(lngEnd, Prompt, "$") + 1
lngEnd = InStr(lngBegin, Prompt, Chr$(13))
srngDataRow.Cells(i_SourceCell) = f.Substitute(Mid$(Prompt, lngBegin, lngEnd - lngBegin), "$", "")
Set srngDataRow = srngDataRow.Offset(1)
Next i
For i = 1 To slngSameSheetCount
srngDataRow.Cells(i_TargetCell) = sstrTargetCell
srngDataRow.Cells(i_TargetSheet) = sstrTargetSheet
lngBegin = InStr(lngEnd, Prompt, "]") + 1
lngEnd = InStr(lngBegin, Prompt, "'")
srngDataRow.Cells(i_SourceSheet) = Mid$(Prompt, lngBegin, lngEnd - lngBegin)
lngBegin = InStr(lngEnd, Prompt, "$") + 1
lngEnd = InStr(lngBegin, Prompt, Chr$(13))
If lngEnd = 0 Then lngEnd = Len(Prompt) + 1
srngDataRow.Cells(i_SourceCell) = f.Substitute(Mid$(Prompt, lngBegin, lngEnd - lngBegin), "$", "")
Set srngDataRow = srngDataRow.Offset(1)
Next i
slngState = slngState + 1
MsgBoxInterceptor = vbOK
Case 2: ' Just skipping through
slngState = slngState + 1
MsgBoxInterceptor = vbYes
Case 3: 'Get other book sources (STILL TODO)
lngEnd = InStr(1, Prompt, "")
For i = 1 To slngClosedBookCount
srngDataRow.Cells(i_TargetCell) = sstrTargetCell
srngDataRow.Cells(i_TargetSheet) = sstrTargetSheet
' lngBegin = InStr(lngEnd, Prompt, "]") + 1
' lngEnd = InStr(lngBegin, Prompt, "'")
' srngDataRow.Cells(i_SourceSheet) = Mid$(Prompt, lngBegin, lngEnd - lngBegin)
'
' lngBegin = InStr(lngEnd, Prompt, "$") + 1
' lngEnd = InStr(lngBegin, Prompt, Chr$(13))
' srngDataRow.Cells(i_SourceCell) = f.Substitute(Mid$(Prompt, lngBegin, lngEnd - lngBegin), "$", "")
Set srngDataRow = srngDataRow.Offset(1)
Next i
For i = 1 To slngOpenBookCount
srngDataRow.Cells(i_TargetCell) = sstrTargetCell
srngDataRow.Cells(i_TargetSheet) = sstrTargetSheet
' lngBegin = InStr(lngEnd, Prompt, "]") + 1
' lngEnd = InStr(lngBegin, Prompt, "'")
' srngDataRow.Cells(i_SourceSheet) = Mid$(Prompt, lngBegin, lngEnd - lngBegin)
'
' lngBegin = InStr(lngEnd, Prompt, "$") + 1
' lngEnd = InStr(lngBegin, Prompt, Chr$(13))
' If lngEnd = 0 Then lngEnd = Len(Prompt) + 1
' srngDataRow.Cells(i_SourceCell) = f.Substitute(Mid$(Prompt, lngBegin, lngEnd - lngBegin), "$", "")
Set srngDataRow = srngDataRow.Offset(1)
Next i
slngState = slngState + 1
MsgBoxInterceptor = vbOK
Case 4: ' Finished -> tidy up
srngDataRow.EntireColumn.AutoFit
slngState = 0
MsgBoxInterceptor = vbCancel
Case Else
End Select
End Function
Explanation:
The key to this code is the use of static variables, created with the Static keyword. These retain their values even after VBA stops running and is restarted. They are used in the code to allow a state machine to be constructed, which mimics a set sequence of user interaction with the message boxes.
The rest is just string parsing of the MsgBox messages.
EDIT: (v0.2) Now displays error messages.
EDIT: (v0.3) Now does a full trace back to hard-coded values.
All fun aside, if you're serious about tracing all the way back to a hard-coded value, the best way is to write a main RunMe_Controller sub to control the original code. Together with a hook function (and some helper function), this is actually the simplest way to leverage the existing code.
The MsgBoxInterceptor() function is smart enough to allow error messages through but silently traps all other MsgBox() calls.
See the section at the bottom of the answer for further important details.
Installation:
Copy/paste the new bug-fixed RunMe code block to a module;
Insert v0.3 of the following updated code block into the previous code where indicated;
Do a "Current Module", "Find Whole Words Only" search for MsgBox with replacement MsgBoxInterceptor;
Add the following two references to the VBA project.
Microsoft VBScript Regular Expressions 5.5
Microsoft Scripting Runtime
Code:
'===============================================================================
' Module : <in any standard module>
' Version : 0.3
' Part : 1 of 1
' References : Microsoft VBScript Regular Expressions 5.5
' : Microsoft Scripting Runtime
' Online : https://stackoverflow.com/a/46036068/1961728
'===============================================================================
Private Const l_No_transformation As String = "No transformation"
Private Enum i_
z__NONE = 0
SourceCell
SourceSheet
SourceBook
TargetCell
TargetSheet
TargetBook
Formula
Index
SourceRef
z__NEXT
z__FIRST = z__NONE + 1
z__LAST = z__NEXT - 1
End Enum
Private meMsgBoxResult As VBA.VbMsgBoxResult
'v0.3
Public Sub RunMe_Controller()
Const s_Headers As String = "Source Cell::Source Sheet::Source Book::Target Cell::Target Sheet::Target Book::Formula"
Const s_Separator As String = "::"
Const l_Circular As String = "Circular"
Dim ƒ As Excel.WorksheetFunction: Set ƒ = Excel.WorksheetFunction
Dim dictFullRefTrace As Scripting.Dictionary '##Early Bound## As Object
Dim varRootRef As Variant
Dim varTargetRef As Variant
Dim varSavedTraceStepKey As Variant
Dim varNewTraceStep As Variant
Dim strNewKey As String
Application.ScreenUpdating = False 'Set to true for psychedelic display
Set dictFullRefTrace = New Dictionary '##Early Bound## = CreateObject("Scripting.Dictionary")
varRootRef = ActiveCell.Address(External:=True)
dictFullRefTrace.Add varRootRef & s_Separator & s_Separator, TheRefTraceStepAsArray(varRootRef)
dictFullRefTrace.Add s_Separator & s_Separator, TheRefTraceStepAsArray() 'Need two trace steps in dict to start dynamic expansion
For Each varSavedTraceStepKey In dictFullRefTrace: Do ' Can't use .Items as it is not dynamically expanded
If varSavedTraceStepKey = s_Separator & s_Separator Then ' Dummy trace step (dict exhausted) -> clean up fake trace steps
dictFullRefTrace.Remove varRootRef & s_Separator & s_Separator
dictFullRefTrace.Remove s_Separator & s_Separator
Exit Do
End If
varTargetRef = dictFullRefTrace(varSavedTraceStepKey)(i_.SourceRef)
Select Case True
Case varTargetRef Like "'?:*": ' Closed Wb -> ignore for now (TODO - auto open it)
Exit Do
Case varSavedTraceStepKey Like "*#": ' "No transformation" (from its own trace step) -> ignore
Exit Do
Case varSavedTraceStepKey Like "*" & l_Circular: ' "Circular" (from its own trace step) -> ignore
Exit Do
End Select
meMsgBoxResult = vbOK
FindCellPrecedents Evaluate(varTargetRef) ' ~= RunMe() - leverage the existing code to update the global Ref Collections
Select Case meMsgBoxResult
Case vbOK:
For Each varNewTraceStep In TheNewTraceSteps(fromTarget:=varTargetRef).Items
strNewKey = varNewTraceStep(i_.SourceRef) & s_Separator & varTargetRef & s_Separator
If dictFullRefTrace.Exists(strNewKey) Then ' Target is a circular ref -> mark it and then add it
strNewKey = strNewKey & l_Circular
varNewTraceStep(i_.Formula) = l_Circular
End If
If Not dictFullRefTrace.Exists(strNewKey) Then ' Ignore subsequent circular refs for this target
dictFullRefTrace.Add strNewKey, varNewTraceStep
End If
Next varNewTraceStep
Case vbIgnore: ' No transformation - typically occurs multiple times, so need multiple unique keys
varNewTraceStep = TheRefTraceStepAsArray(varTargetRef, varTargetRef)
strNewKey = varTargetRef & s_Separator & varTargetRef & s_Separator & varNewTraceStep(i_.Index)
dictFullRefTrace.Add strNewKey, varNewTraceStep
Case vbAbort: ' Error occurred and message was displayed
Exit Sub
Case Else
' Never
End Select
' Move dummy trace step to end
dictFullRefTrace.Remove s_Separator & s_Separator
dictFullRefTrace.Add s_Separator & s_Separator, vbNullString
Loop While 0: Next varSavedTraceStepKey
' Create, fill and format worksheet
With Evaluate(varRootRef)
.Worksheet.Parent.Activate
Worksheets.Add after:=.Worksheet
End With
With ActiveSheet.Rows(1).Resize(ColumnSize:=i_.Index - i_.z__FIRST + 1)
.Value2 = Split(s_Headers, s_Separator)
.Font.Bold = True
With .Offset(1).Resize(RowSize:=dictFullRefTrace.Count)
.Cells.Value = ƒ.Transpose(ƒ.Transpose(dictFullRefTrace.Items)) ' Fill
.Sort .Columns(i_.Index), xlDescending, Header:=xlNo
End With
With .EntireColumn
.Columns(i_.Formula).Copy
.Columns(i_.Index).PasteSpecial Paste:=xlPasteValues
.Columns(i_.Formula).Delete
.Columns(i_.SourceCell).HorizontalAlignment = xlCenter
.Columns(i_.TargetCell).HorizontalAlignment = xlCenter
.AutoFilter i_.Formula, l_Circular
.Columns(i_.Formula).SpecialCells(xlCellTypeConstants).Font.Color = vbRed
.AutoFilter i_.Formula, l_No_transformation
.Columns(i_.Formula).SpecialCells(xlCellTypeConstants).Font.Bold = True
.AutoFilter
.Rows(1).Font.ColorIndex = xlAutomatic
.AutoFit
End With
.Cells(1).Select
End With
Application.ScreenUpdating = True
End Sub
Private Function TheNewTraceSteps _
( _
Optional ByRef fromTarget As Variant _
) _
As Scripting.Dictionary '##Early Bound## As Object
Dim pvarTargetRef As Variant: pvarTargetRef = fromTarget
Dim mtchMultiCellAddress As VBScript_RegExp_55.Match '##Early Bound## As Object
Dim strFormula As String
Dim rngCell As Range
Dim strKey As String
Dim astrTraceStep() As String
Dim varRunMeSourceRef As Variant
Dim varRefCollection As Variant
Set TheNewTraceSteps = New Dictionary '##Early Bound## = CreateObject("Scripting.Dictionary")
strFormula = Evaluate(pvarTargetRef).Formula
With New VBScript_RegExp_55.RegExp '##Early Bound## = CreateObject("VBScript_RegExp_55.RegExp")
.Global = True
.Pattern = "(?:(?:[:]| *)(?:\$?[A-Z]{1,3}\d+:\$?[A-Z]{1,3}\d+))+"
If .test(strFormula) Then
For Each mtchMultiCellAddress In .Execute(strFormula)
For Each rngCell In Evaluate(mtchMultiCellAddress.Value)
strKey = rngCell.Address
If Not TheNewTraceSteps.Exists(strKey) Then
astrTraceStep = TheRefTraceStepAsArray(rngCell.Address(External:=True), pvarTargetRef)
TheNewTraceSteps.Add strKey, astrTraceStep
End If
Next rngCell
Next mtchMultiCellAddress
End If
End With
For Each varRefCollection In Array(SameWbSameSheetRefs, SameWbOtherSheetRefs, OtherWbRefs)
For Each varRunMeSourceRef In varRefCollection
strKey = Evaluate(varRunMeSourceRef).Address
If Not TheNewTraceSteps.Exists(strKey) Then
astrTraceStep = TheRefTraceStepAsArray(varRunMeSourceRef, pvarTargetRef)
TheNewTraceSteps.Add strKey, astrTraceStep
End If
varRefCollection.Remove 1
Next varRunMeSourceRef
Next varRefCollection
End Function
Private Function TheRefTraceStepAsArray _
( _
Optional ByRef SourceRef As Variant = vbNullString, _
Optional ByRef TargetRef As Variant = vbNullString _
) _
As String()
Static slngIndex As Long ' Required for reverse ordering of trace output
Dim pvarSourceRef As String: pvarSourceRef = Replace(SourceRef, "''", "'")
Dim pvarTargetRef As String: pvarTargetRef = Replace(TargetRef, "''", "'")
Dim astrTraceStepValues() As String: ReDim astrTraceStepValues(1 To i_.z__LAST)
Dim strFormula As String: strFormula = vbNullString
Dim astrSourceCellSheetBook() As String
Dim astrTargetCellSheetBook() As String
astrSourceCellSheetBook = Ref2CellSheetBook(pvarSourceRef)
astrTargetCellSheetBook = Ref2CellSheetBook(pvarTargetRef)
If pvarSourceRef = vbNullString _
Or pvarTargetRef = vbNullString _
Then
' slngIndex = 0 ' Dummy or root ref, i.e., new trace started -> intialize static variable
Else
slngIndex = slngIndex + 1
With Evaluate(TargetRef)
strFormula = IIf(.HasFormula And pvarSourceRef <> pvarTargetRef, "'" & Mid$(.Formula, 2), l_No_transformation)
End With
End If
astrTraceStepValues(i_.SourceCell) = astrSourceCellSheetBook(1)
astrTraceStepValues(i_.SourceSheet) = astrSourceCellSheetBook(2)
astrTraceStepValues(i_.SourceBook) = astrSourceCellSheetBook(3)
astrTraceStepValues(i_.TargetCell) = astrTargetCellSheetBook(1)
astrTraceStepValues(i_.TargetSheet) = astrTargetCellSheetBook(2)
astrTraceStepValues(i_.TargetBook) = astrTargetCellSheetBook(3)
astrTraceStepValues(i_.Formula) = strFormula
astrTraceStepValues(i_.Index) = slngIndex
astrTraceStepValues(i_.SourceRef) = SourceRef
TheRefTraceStepAsArray = astrTraceStepValues
End Function
Private Function Ref2CellSheetBook(ByRef Ref As Variant) As String()
Dim × As Long: × = 4
Dim astrCellSheetBook() As String: ReDim astrCellSheetBook(1 To i_.z__LAST)
If IsMissing(Ref) Then GoTo ExitFunction:
× = × - 1: astrCellSheetBook(×) = Mid$(Ref, InStr(Ref, "[") + 1, Abs(InStr(Ref, "]") - InStr(Ref, "[") - 1))
× = × - 1: astrCellSheetBook(×) = Mid$(Ref, InStr(Ref, "]") + 1, Abs(InStr(Ref, "!") - InStr(Ref, "]") - 2))
× = × - 1: astrCellSheetBook(×) = Mid$(Ref, InStr(Ref, "!") + 1)
astrCellSheetBook(×) = Replace(astrCellSheetBook(×), "$", "")
ExitFunction:
Ref2CellSheetBook = astrCellSheetBook
End Function
Private Function MsgBoxInterceptor _
( _
Prompt, _
Optional Buttons As VbMsgBoxStyle = vbOKOnly, _
Optional Title, _
Optional HelpFile, _
Optional Context _
) _
As VBA.VbMsgBoxResult
If Buttons = vbOKOnly _
Then
If Prompt Like "*does not contain a formula*" _
Or Prompt Like "*contains a formula with no precedents*" _
Then
meMsgBoxResult = vbIgnore
Else
meMsgBoxResult = vbAbort
MsgBox Prompt, Buttons, Title, HelpFile, Context
End If
End If
MsgBoxInterceptor = vbCancel
End Function
Bug-fixed original code:
Option Explicit
Public OtherWbRefs As Collection
Public ClosedWbRefs As Collection
Public SameWbOtherSheetRefs As Collection
Public SameWbSameSheetRefs As Collection
Public CountOfClosedWb As Long
Dim headerString As String
' <-- Insert other code here
Sub RunMe()
Call FindCellPrecedents(ActiveCell)
End Sub
Sub FindCellPrecedents(homeCell As Range)
Dim i As Long, j As Long, pointer As Long
Dim maxReferences As Long
Dim outStr As String
Dim userInput As Long
If homeCell.HasFormula Then
Set OtherWbRefs = New Collection: CountOfClosedWb = 0
Set SameWbOtherSheetRefs = New Collection
Set SameWbSameSheetRefs = New Collection
Rem find closed precedents from formula String
Call FindClosedWbReferences(homeCell)
Rem find Open precedents from navigate arrows
homeCell.Parent.ClearArrows
homeCell.ShowPrecedents
headerString = "in re: the formula in " & homeCell.Address(, , , True)
maxReferences = Int(Len(homeCell.Formula) / 3) + 1
On Error GoTo LoopOut:
For j = 1 To maxReferences
homeCell.NavigateArrow True, 1, j
If ActiveCell.Address(, , , True) = homeCell.Address(, , , True) Then
Rem closedRef
Call CategorizeReference("<ClosedBook>", homeCell)
Else
Call CategorizeReference(ActiveCell, homeCell)
End If
Next j
LoopOut:
On Error GoTo 0
For j = 2 To maxReferences
homeCell.NavigateArrow True, j, 1
If ActiveCell.Address(, , , True) = homeCell.Address(, , , True) Then Exit For
Call CategorizeReference(ActiveCell, homeCell)
Next j
homeCell.Parent.ClearArrows
Rem integrate ClosedWbRefs (from parsing) With OtherWbRefs (from navigation)
If ClosedWbRefs.Count <> CountOfClosedWb Then '#robinCTS#' Should read (ParsedClosedWbRefs <> CountOfNavigatedClosedWbRefs)
If ClosedWbRefs.Count = 0 Then
MsgBoxInterceptor homeCell.Address(, , , True) & " contains a formula with no precedents."
Exit Sub
Else
MsgBoxInterceptor "string-" & ClosedWbRefs.Count & ":nav " & CountOfClosedWb
MsgBoxInterceptor "Methods find different # of closed precedents."
End
End If
End If
pointer = 1
For j = 1 To OtherWbRefs.Count
If OtherWbRefs(j) Like "<*" Then
OtherWbRefs.Add Item:=ClosedWbRefs(pointer), Key:="closed" & CStr(pointer), after:=j
pointer = pointer + 1
OtherWbRefs.Remove j
End If
Next j
Rem present findings
outStr = homeCell.Address(, , , True) & " contains a formula with:"
outStr = outStr & vbCrLf & vbCrLf & CountOfClosedWb & " precedents in closed workbooks."
outStr = outStr & vbCr & (OtherWbRefs.Count - CountOfClosedWb) & " precedents in other workbooks that are open."
outStr = outStr & vbCr & SameWbOtherSheetRefs.Count & " precedents on other sheets in the same workbook."
outStr = outStr & vbCr & SameWbSameSheetRefs.Count & " precedents on the same sheet."
outStr = outStr & vbCrLf & vbCrLf & "YES - See details about Other Books."
outStr = outStr & vbCr & "NO - See details about The Active Book."
Do
userInput = MsgBoxInterceptor(Prompt:=outStr, Title:=headerString, Buttons:=vbYesNoCancel + vbDefaultButton3)
Select Case userInput
Case Is = vbYes
MsgBoxInterceptor Prompt:=OtherWbDetail(), Title:=headerString, Buttons:=vbOKOnly
Case Is = vbNo
MsgBoxInterceptor Prompt:=SameWbDetail(), Title:=headerString, Buttons:=vbOKOnly
End Select
Loop Until userInput = vbCancel
Else
MsgBoxInterceptor homeCell.Address(, , , True) & vbCr & " does not contain a formula."
End If
End Sub
Sub CategorizeReference(Reference As Variant, Home As Range)
Rem assigns reference To the appropriate collection
If TypeName(Reference) = "String" Then
Rem String indicates reference To closed Wb
OtherWbRefs.Add Item:=Reference, Key:=CStr(OtherWbRefs.Count)
CountOfClosedWb = CountOfClosedWb + 1
Else
If Home.Address(, , , True) = Reference.Address(, , , True) Then Exit Sub '#robinCTS#' Never true as same check done in caller
If Home.Parent.Parent.Name = Reference.Parent.Parent.Name Then
Rem reference In same Wb
If Home.Parent.Name = Reference.Parent.Name Then
Rem sameWb sameSheet
SameWbSameSheetRefs.Add Item:=Reference.Address(, , , True), Key:=CStr(SameWbSameSheetRefs.Count)
Else
Rem sameWb Other sheet
SameWbOtherSheetRefs.Add Item:=Reference.Address(, , , True), Key:=CStr(SameWbOtherSheetRefs.Count)
End If
Else
Rem reference To other Open Wb
OtherWbRefs.Add Item:=Reference.Address(, , , True), Key:=CStr(OtherWbRefs.Count)
End If
End If
End Sub
Sub FindClosedWbReferences(inRange As Range) '#robinCTS#' Should read FindParsedOtherWbReferences
Rem fills the collection With closed precedents parsed from the formula String
Dim testString As String, returnStr As String, remnantStr As String
testString = inRange.Formula
Set ClosedWbRefs = New Collection
Do
returnStr = NextClosedWbRefStr(testString, remnantStr)
ClosedWbRefs.Add Item:=returnStr, Key:=CStr(ClosedWbRefs.Count)
testString = remnantStr
Loop Until returnStr = vbNullString '#robinCTS#' Better if add " Or testString = vbNullString"
ClosedWbRefs.Remove ClosedWbRefs.Count '#robinCTS#' then this no longer required
End Sub
Function NextClosedWbRefStr(FormulaString As String, Optional ByRef Remnant As String) As String
Dim workStr As String
Dim start As Long, interval As Long, del As Long
For start = 1 To Len(FormulaString)
For interval = 2 To Len(FormulaString) - start + 1
workStr = Mid(FormulaString, start, interval)
If workStr Like Chr(39) & "[![]*[[]*'![$A-Z]*#" Then '#robinCTS#' Original was "[!!]*'![$A-Z]*#"
If workStr Like Chr(39) & "[![]*[[]*'!*[$1-9A-Z]#" Then '#robinCTS#' Original was "[!!]*'!*[$1-9A-Z]#" Not required?
interval = interval - CLng(Mid(FormulaString, start + interval, 1) Like "#") '#robinCTS#' Not required as always Like "*#" here?
interval = interval - 3 * CLng(Mid(FormulaString, start + interval, 1) = ":")
interval = interval - CLng(Mid(FormulaString, start + interval, 1) Like "[$1-9A-Z]")
interval = interval - CLng(Mid(FormulaString, start + interval, 1) Like "[$1-9A-Z]")
interval = interval - CLng(Mid(FormulaString, start + interval, 1) Like "[$1-9A-Z]")
interval = interval - CLng(Mid(FormulaString, start + interval, 1) Like "[$1-9A-Z]")
NextClosedWbRefStr = Mid(FormulaString, start, interval)
Remnant = Mid(FormulaString, start + interval)
Exit Function
End If
End If
Next interval
Next start
End Function
Function OtherWbDetail() As String
Rem display routine
OtherWbDetail = OtherWbDetail & "There are " & OtherWbRefs.Count & " references to other workbooks. "
OtherWbDetail = OtherWbDetail & IIf(CBool(CountOfClosedWb), CountOfClosedWb & " are closed.", vbNullString)
OtherWbDetail = OtherWbDetail & vbCr & "They appear in the formula in this order:" & vbCrLf & vbCrLf
OtherWbDetail = OtherWbDetail & rrayStr(OtherWbRefs, vbCr)
End Function
Function SameWbDetail() As String
Rem display routine
SameWbDetail = SameWbDetail & "There are " & SameWbOtherSheetRefs.Count & " ref.s to other sheets in the same book."
SameWbDetail = SameWbDetail & vbCr & "They appear in this order, including duplications:" & vbCrLf & vbCrLf
SameWbDetail = SameWbDetail & rrayStr(SameWbOtherSheetRefs, vbCr)
SameWbDetail = SameWbDetail & vbCrLf & vbCrLf & "There are " & SameWbSameSheetRefs.Count & " precedents on the same sheet."
SameWbDetail = SameWbDetail & vbCr & "They are (out of order, duplicates not noted):" & vbCrLf & vbCrLf
SameWbDetail = SameWbDetail & rrayStr(SameWbSameSheetRefs, vbCr)
End Function
Function rrayStr(ByVal inputRRay As Variant, Optional Delimiter As String)
Rem display routine
Dim xVal As Variant
If IsEmpty(inputRRay) Then Exit Function
If Delimiter = vbNullString Then Delimiter = " "
For Each xVal In inputRRay
rrayStr = rrayStr & Delimiter & xVal
Next xVal
rrayStr = Mid(rrayStr, Len(Delimiter) + 1)
End Function
Issues:
Closed workbooks are not auto-opened (yet)
Formulas referencing closed workbooks will display the pathname
Formulas referencing open workbooks won't display the pathname, unlike your example
Only expands simple hard-coded multi-cell ranges (for now)
Doesn't expand whole columns or rows, yet, only grabs the first cell
Doesn't find/expand INDEX, OFFSET or any other similar calculated ranges
Expanded ranges are not sorted any may not be ordered nicely.
Features/Enhancements:
RunMe code bugfixes now allow proper detection of closed workbook refs as requested
Simple multi-cell ranges now expand out as requested
Circular references are properly accounted for
Hard-coded values show a bold "No transformation" as requested
Hard-coded values display multiple times if accessed from multiple targets
Apostrophes in sheet names are properly taken care of
Note: If you are curious about my variable naming convention, it is based on RVBA.

Excel VBA - Split a cell into 1000 pieces and copy them into different cells

I was wondering if there is a way to split a cell with for example 6000 words into 1000 word pieces. So for example, 1000 words in cell C1, then the next 1000 words in C2 and so on.
Here is the code I have so far.
The output of that code (Cell C1) should be split, with C6 with 1000 words, C7 with 1000 words and so on until no more words are available.
Thank you in advance!
Option Explicit
Option Base 1
Dim dStr As String, aCell As Range
Dim cet, i As Long
Sub countWords()
Application.ScreenUpdating = False
Dim iniWords As Long, lWords As Long
Dim wK As Worksheet
On Error GoTo Err
Set wK = ActiveSheet
dStr = Join(WorksheetFunction.Transpose(wK.Range("A1:A" & wK.Range("A" & Rows.Count).End(xlUp).Row).Value), " ")
'iniWords = WorksheetFunction.CountA(wK.Range("A1:A" & wK.Rows.Count))
cet = Split(dStr, " ")
iniWords = UBound(cet)
wK.Range("A1:A" & wK.Range("A" & Rows.Count).End(xlUp).Row).RemoveDuplicates Columns:=1, Header:=xlNo
'lWords = WorksheetFunction.CountA(wK.Range("A1:A" & wK.Rows.Count))
dStr = Join(WorksheetFunction.Transpose(wK.Range("A1:A" & wK.Range("A" & Rows.Count).End(xlUp).Row).Value), " ")
cet = Split(dStr, " ")
dStr = ""
For i = LBound(cet) To UBound(cet)
If Trim(cet(i)) <> "" And InStr(dStr, Trim(cet(i))) = 0 Then
dStr = Trim(dStr) & " " & Trim(cet(i))
End If
Next i
dStr = Trim(dStr)
cet = Split(dStr, " ")
lWords = UBound(cet)
wK.Range("C1") = dStr
Application.ScreenUpdating = True
MsgBox "Words: " & iniWords & vbNewLine & _
"Removed duplicates " & iniWords - lWords & vbNewLine & _
"Remaining Words " & lWords
Exit Sub
Err:
MsgBox "There is no data in row A"
End Sub
you could use this:
Option Explicit
Sub main()
Const NWORDS As Long = 100 '<--| it's the number of words you want each cell to be written with. change it to your needs
Dim strng As String
Dim rowOffset As Long
With Range("C1")
strng = .Value
rowOffset = 5 '<--| point to C7 at the first iteration
Do
strng = Replace(strng, " ", "|", , NWORDS) '<--| "mark" the first NWORDS with a different separator (be sure pipe ("|") is not a character you can have in your words)
.Offset(rowOffset).Value = Replace(Left(strng, InStrRev(strng, "|") - 1), "|", " ") '<--| write first NWORDS words in current 'rowoffset' cell
strng = Right(strng, Len(strng) - InStrRev(strng, "|"))
rowOffset = rowOffset + 1 '<--| update row offset
Loop While UBound(Split(strng, " ")) > NWORDS - 1
.Offset(rowOffset).Value = strng
End With
End Sub

Improve VBA flexibility to convert VLOOKUP to INDEX/MATCH

After all my searching for code to read in a VLOOKUP formula and converting it to INDEX/MATCH came up empty, I wrote some myself.
However, the code (below) is lacking some of the flexibility I would like, but I can't seem to figure out how to make it work. Specifically, I would like to test each range criterion in the VLOOKUP formula for being an absolute reference or not, i.e. preceded by $, and carry that through to the INDEX/MATCH formula that results. For example, the formula =VLOOKUP(A2,$A$1:B$11,2,FALSE) should convert to =INDEX(B$1:B$11,MATCH(A2,$A1:$A11,0)).
NOTE: This sub depends on two functions (ColumnLetterToNumber and ColumnNumberToLetter). As their names imply they take column letters or numbers and interconvert them. Both these functions are short, simple, and work without problems. However, if anyone believes that the code to one or both of them would be helpful, I would be happy to provide them.
Additionally, any ideas on improving code readability and/or execution efficiency would also be appreciated.
Option Explicit
Public Sub ConvertToIndex()
Dim booLookupType As Boolean
Dim booLeftOfColon As Boolean
Dim booHasRowRef As Boolean
Dim lngStartCol As Long
Dim lngRefCol As Long
Dim lngStart As Long
Dim lngEnd As Long
Dim lngMatchType As Long
Dim lngInt As Long
Dim lngRowRef As Long
Dim strRefCol As String
Dim strOldFormula As String
Dim strNewFormula As String
Dim strLookupCell As String
Dim strValueCol As String
Dim strMatchCol As String
Dim strStartRow As String
Dim strEndRow As String
Dim strCheck As String
Dim strLookupRange As String
Dim strTabRef As String
Dim strSheetRef As String
Dim rngToMod As Range
Dim rngModCell As Range
Set rngToMod = Selection
For Each rngModCell In rngToMod
strOldFormula = rngModCell.Formula
lngStart = InStrRev(strOldFormula, "VLOOKUP(")
If lngStart > 0 Then
lngStart = InStr(lngStart, strOldFormula, "(") + 1
lngEnd = InStr(lngStart, strOldFormula, ",")
strLookupCell = Mid(strOldFormula, lngStart, lngEnd - lngStart)
lngStart = lngEnd + 1
lngEnd = InStr(lngStart, strOldFormula, ",")
strLookupRange = Mid(strOldFormula, lngStart, lngEnd - lngStart)
lngStart = lngEnd + 1
lngEnd = InStr(lngStart, strOldFormula, ",")
lngRefCol = CInt(Mid(strOldFormula, lngStart, lngEnd - lngStart))
lngStart = lngEnd + 1
lngEnd = InStr(lngStart, strOldFormula, ")")
booLookupType = (Mid(strOldFormula, lngStart, lngEnd - lngStart) = "TRUE")
If booLookupType Then
lngMatchType = 1
Else
lngMatchType = 0
End If
booLeftOfColon = True
lngEnd = InStr(1, strLookupRange, "]")
If lngEnd > 0 Then
strSheetRef = Left(strLookupRange, lngEnd)
strLookupRange = Right(strLookupRange, Len(strLookupRange) - lngEnd)
Else
strSheetRef = ""
End If
lngEnd = InStr(1, strLookupRange, "!")
If lngEnd > 0 Then
strTabRef = Left(strLookupRange, lngEnd)
strLookupRange = Right(strLookupRange, Len(strLookupRange) - lngEnd)
Else
strTabRef = ""
End If
For lngInt = 1 To Len(strLookupRange)
strCheck = Mid(strLookupRange, lngInt, 1)
Select Case True
Case strCheck = ":"
booLeftOfColon = False
Case booLeftOfColon
If IsNumeric(strCheck) Then
strStartRow = strStartRow & strCheck
Else
strMatchCol = strMatchCol & strCheck
End If
Case Else
If IsNumeric(strCheck) Then strEndRow = strEndRow & strCheck
End Select
Next lngInt
strMatchCol = Replace(strMatchCol, "$", "")
lngStartCol = ColumnLetterToNumber(strMatchCol)
strValueCol = ColumnNumberToLetter(lngStartCol + lngRefCol - 1)
If Len(strStartRow) > 0 Then strStartRow = "$" & strStartRow
If Len(strEndRow) > 0 Then strEndRow = "$" & strEndRow
strValueCol = strSheetRef & strTabRef & strValueCol & strStartRow & ":" & strValueCol & strEndRow
strMatchCol = strSheetRef & strTabRef & strMatchCol & strStartRow & ":" & strMatchCol & strEndRow
strNewFormula = "=INDEX(" & strValueCol & ",MATCH(" & "$" & strLookupCell & "," & strMatchCol & "," & lngMatchType & "))"
rngModCell.Formula = strNewFormula
End If
Next rngModCell
End Sub
At this time I am not looking for help to take this to the next step of enabling it to process VLOOKUP/HLOOKUP or VLOOKUP/MATCH combination formulas.
To avoid all errors I can think of, you would need to change it to a not so good looking way like this:
Sub changeToIndex()
Dim xText As Boolean
Dim xBrac As Long
Dim VLSep As New Collection
Dim i As Long, t As String
With Selection.Cells(1, 1) 'just for now
'it assumes that there is NEVER a text string which has VLOOKUP like =A1&"mean text with VLOOKUP inside it"
While InStr(1, .Formula, "VLOOKUP", vbTextCompare)
Set VLSep = New Collection
VLSep.Add " " & InStr(1, .Formula, "VLOOKUP", vbTextCompare) + 7
'get the parts
For i = VLSep(1) + 1 To Len(.Formula)
t = Mid(.Formula, i, 1)
If t = """" Then
xText = Not xText
ElseIf Not xText Then 'avoid "(", ")" and "," inside of the string to be count
If t = "(" Then
xBrac = xBrac + 1
ElseIf xBrac Then 'cover up if inside of other functions
If t = ")" Then xBrac = xBrac - 1
ElseIf t = ")" Then
VLSep.Add " " & i
Exit For
ElseIf t = "," Then
VLSep.Add " " & i 'the space is to avoid errors with index and item if both are numbers
End If
End If
Next
Dim xFind As String 'get all the parts
Dim xRng As String
Dim xCol As String
Dim xType As String
xFind = Mid(.Formula, VLSep(1) + 1, VLSep(2) - VLSep(1) - 1)
xRng = Mid(.Formula, VLSep(2) + 1, VLSep(3) - VLSep(2) - 1)
xCol = Mid(.Formula, VLSep(3) + 1, VLSep(4) - VLSep(3) - 1)
If VLSep.Count = 5 Then
xType = Mid(.Formula, VLSep(4) + 1, VLSep(5) - VLSep(4) - 1)
Else
xType = "0"
End If
Dim fullFormulaNew As String 'get the whole formulas
Dim fullFormulaOld As String
fullFormulaNew = "INDEX(" & xRng & ",MATCH(" & xFind & ",INDEX(" & xRng & ",,1)," & xType & ")," & xCol & ")"
fullFormulaOld = Mid(Selection.Cells(1, 1).Formula, VLSep(1) - 7, VLSep(VLSep.Count) - VLSep(1) + 8)
.Formula = Replace(.Formula, fullFormulaOld, fullFormulaNew) 'simply replace the old one with the new one
Wend
End With
End Sub
It also should work for very complex formulas. Still you would need some special checks to cut everything so it looks like you want. I just assumed that the range for the vlookup may be something like IF(A1=1,B1:C10,L5:N30) and this said, you would need additional subs to also clear something like this up. :(
A formula like
=VLOOKUP(VLOOKUP(IF(TRUE,A2,"aaa"),$A$1:B$11,2),$B$1:$C$11,2,FALSE)
will be changed (messed up) this way to
=INDEX($B$1:$C$11,MATCH(INDEX($A$1:B$11,MATCH(IF(TRUE,A2,"aaa"),INDEX($A$1:B$11,,1),0),2),INDEX($B$1:$C$11,,1),FALSE),2)
EDIT
Assuming your formulas are "normal" you can replace the the last part with:
Dim xFind As String 'get all the parts
Dim xRngI As String, xRngM As String
Dim xCol As String
Dim xType As String
xFind = Mid(.Formula, VLSep(1) + 1, VLSep(2) - VLSep(1) - 1)
xRngI = Mid(.Formula, VLSep(2) + 1, VLSep(3) - VLSep(2) - 1)
xCol = Mid(.Formula, VLSep(3) + 1, VLSep(4) - VLSep(3) - 1)
If VLSep.Count = 5 Then
xType = Mid(.Formula, VLSep(4) + 1, VLSep(5) - VLSep(4) - 1)
Else
xType = "0"
End If
If xType = "FALSE" Then xType = 0
Do While Not IsNumeric(xCol)
Select Case MsgBox("Error: The Column to pick from is not numerical! Do you want to manually set the column (Yes) or directly use the last column of the input range (No)?", vbYesNoCancel)
Case vbYes
xCol = Application.InputBox("Input the column number for the input range (" & xRngI & "). '1' will be the range " & Range(xRngI).Columns(1).Address(0, 0) & ".", "Column to pick from", 1, , , , , 2)
Case vbNo
xCol = Range(xRngI).Columns.Count
Case vbCancel
xCol = " "
Exit Do
End Select
If xCol <> CInt(xCol) Or xCol > Range(xRngI).Columns.Count Or xCol = 0 Then xCol = " "
Loop
If IsNumeric(xCol) Then
Dim absRs As Boolean, absRe As Boolean, absCs As Boolean, absCe As Boolean
absCs = (Left(xRngI, 1) = "$")
absCe = (Mid(xRngI, InStr(xRngI, ":") + 1, 1) = "$")
absRs = (InStr(2, Left(xRngI, InStr(xRngI, ":") - 1), "$") > 0)
absRe = (InStr(Mid(xRngI, InStr(xRngI, ":") + 2), "$") > 0)
xRngM = Range(xRngI).Columns(1).Cells(1, 1).Address(absRs, absCs) & ":" & Range(xRngI).Columns(1).Cells(Range(xRngI).Rows.Count, 1).Address(absRe, absCs) 'for MATCH
xRngI = Range(xRngI).Cells(1, CLng(xCol)).Address(absRs, absCe) & ":" & Range(xRngI).Cells(Range(xRngI).Rows.Count, CLng(xCol)).Address(absRe, absCe) 'for INDEX
Dim fullFormulaNew As String, fullFormulaOld As String
fullFormulaNew = "INDEX(" & xRngI & ",MATCH(" & xFind & "," & xRngM & "," & xType & "))"
fullFormulaOld = Mid(Selection.Cells(1, 1).Formula, VLSep(1) - 7, VLSep(VLSep.Count) - VLSep(1) + 8)
.Formula = Replace(.Formula, fullFormulaOld, fullFormulaNew) 'simply replace the old one with the new one
End If
Wend
End With
End Sub
As you can see: the "simpler" the outcome, the more code you need. If the lookup_range is not just a address, this will fail.
If you still have any questions, just ask ;)

Get specific values from an range and store it in another

I'm trying to type a search term and look into a entire specific range and every time this term has a match the information will be stored in another column.
When I use "Do", "With" or "While" it just stores one result.
Sub MethodFindAllSamples()
Dim rng1 As Range
Dim strSearch As String
index = 11
strSearch = InputBox("Type the model you are looking for, please: ")
Set rng1 = Range("G:G").Find(strSearch, , xlValues, xlPart, xlByRows, False)
If Not rng1 Is Nothing Then
Application.Goto rng1
Model = ActiveCell(1.1)
Content = ActiveCell(1, 4)
FIssues = Range("ER" & ActiveCell.Row + 1).Value
TIssues = Range("ER" & ActiveCell.Row + 1).Value
MsgBox "Model selected: " & Model & vbNewLine & "CS: " & Content & vbNewLine & " Issues found: " & FIssues
Errors = Left(FIssues, 1)
Errors2 = Mid(TIssues, 22, 1)
Cells(index, 1).Value = Mid(Model, 4, 6)
Cells(index, 3).Value = Errors
Cells(index, 4).Value = Errors2
Cells(index, 2).Value = strSearch + Left(Content, 8)
Else
MsgBox strSearch & " This device can't be found, please try again"
End If
End Sub
this an example how you can achieve this
Sub MethodFindAllSamples()
Dim oCell As Range, i&, z&, strSearch$
strSearch = InputBox("Type the model you are looking for, please: ")
i = Cells(Rows.Count, "G").End(xlUp).Row
z = 0
If strSearch <> "" Then
For Each oCell In Range("G1:G" & i)
If Replace(Trim(UCase(oCell.Value)), " ", "") Like "*" & Replace(Trim(UCase(strSearch)), " ", "") & "*" Then
z = z + 1
End If
Next
If z > 0 Then
MsgBox "Range [D] contain: " & z & " iteration of the selected model : " & strSearch
Else
MsgBox "Range [D] does not contain: " & strSearch
End If
Else
MsgBox "Search model not specified!"
End If
End Sub