I currently have a spreadsheet that parses a HL7 message string using "|" as a delimiter. The String that comes before the first "|" becomes the sheet name (Segment). The code executes on each line of the string (Each segment is parsed). The problem is that sometimes there are multiple segments with the same name. So instead of a new sheet being created, all segments are lumped into the same sheet with that name. What I am trying to do is have the code create a new sheet for each segment and if there it is already present, add sheet name with an incremented number.
Sample Message:
MSH|^~\&|SR|500|CL|500|20140804150856-0500||SIU^S14|5009310|P|2.3|||AL|NE|USA
SCH|10262|10262|""|S14^(SCHEDULED)^L|44950^APPENDECTOMY^C4||^^^201408081345-0500^^^^^^2||30|MIN^MINUTES|^^^201408081345-0500^201408081415-0500|10000000034^ROISTAFF^CHIEF^O||||||||
PID|1|5000|50^^^USVHA&&0363^NI^FACILITY ID&500&L^^20140804~666^^^USSSA&&0363^SS^FACILITY ID&500&L~^^^USDOD&&0363^TIN^VA FACILITY ID&500&L~^^^USDOD&&0363^FI^FACILITY ID&500&L~736^^^USVHA&&0363^PI^VA FACILITY ID&500&L|736|DATA^PATIENT^^^^^L||19540214|M|||123 main Street^^SW RS^FL^33332^USA^P^^~^^^^^^N|||||||4221^764|666|||||N||||||N||
PV1|1|I|||||||||||||||||||||||||||||||||||||500|
OBX|1|CE|^SPECIALTY^||^GENERAL||||||S|||||
OBX|2|CE|^PATIENT CLASS^||^INPATIENT^L||||||S|||||
DG1|1|I9|540.1|ABSCESS OF APPENDIX||P
DG1|2|I9||APPENDICITIS||PR
RGS|1|A|
AIS|1|A|44950^APPENDECTOMY^C4||||
AIP|1|A|1000^PHYSICIAN^KT^|^SURGEON^99||||PENDING
AIP|2|A|1000^NURSE^ONE^|^1ST ASST.^99||||PENDING
AIP|3|A|1000^NURSE^TWO^|^2ND ASST.^99||||PENDING
AIP|4|A|1000^ATTENDING^ONE^|^ATT. SURGEON^99||||PENDING
AIP|5|A|115^DATA^PROVIDERONE^|^PRIN. ANES.^99||||PENDING
AIP|6|A|1000^DATA^PATHOLOGIST^|^ANES. SUPER.^||||PENDING
AIL||500^^^OR1|^OPERATING ROOM||||PENDING
Option Explicit
Const HL7_DELIMITER_FIELD = "|"
Const HL7_DELIMITER_SEGMENT = vbLf
Sub DoHL7Parsing(sMessage As String)
Dim vSegments As Variant, vCurSeg As Variant
Dim vFields As Variant, rCurField As Range, iIter As Integer
Dim wsSeg As Worksheet
vSegments = VBA.Split(sMessage, HL7_DELIMITER_SEGMENT)
For Each vCurSeg In vSegments
vFields = VBA.Split(vCurSeg, HL7_DELIMITER_FIELD)
If WorksheetExists(vFields(0), ThisWorkbook) Then
On Error Resume Next
For iIter = 1 To UBound(vFields)
Set rCurField = ThisWorkbook.Worksheets(vFields(0)).Range("A65536").End(xlUp).Offset(1, 0)
rCurField.Value = vFields(0)
rCurField.Offset(0, 1).Value = (rCurField.Row - 1)
rCurField.Offset(0, 2).NumberFormat = "#"
rCurField.Offset(0, 2).Value = vFields(iIter)
Next iIter
On Error Resume Next
ElseIf Not WorksheetExists(vFields(0), ThisWorkbook) Then
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = vFields(0)
For iIter = 1 To UBound(vFields)
Set rCurField = ThisWorkbook.Worksheets(vFields(0)).Range("A65536").End(xlUp).Offset(1, 0)
rCurField.Value = vFields(0)
rCurField.Offset(0, 1).Value = (rCurField.Row - 1)
rCurField.Offset(0, 2).NumberFormat = "#"
rCurField.Offset(0, 2).Value = vFields(iIter)
Next iIter
'MsgBox "Invalid or unkown segment: " & vFields(0)
End If
Next vCurSeg
On Error Resume Next
End Sub
Public Function WorksheetExists(ByVal WorksheetName As String, Optional InWorkbook As Workbook) As Boolean
Dim Sht As Worksheet
WorksheetExists = False
If Not InWorkbook Is Nothing Then
For Each Sht In InWorkbook.Worksheets
If Sht.Name = WorksheetName Then WorksheetExists = True
Next Sht
Else
For Each Sht In ActiveWorkbook.Worksheets
If Sht.Name = WorksheetName Then WorksheetExists = True
Next Sht
End If
On Error Resume Next
End Function
The trick here is to just count the number of sheets whose Left(ShtName,3) value is equal to vFields(0). Based on the count, add 1 and append to end of vField(0). With this approach, you don't even need the dirty On Error Resume Next because you won't be targeting the same sheet twice, which can bring down your line count considerably.
For the sheet counting, add the following function to your module:
Function CountSheetsWithName(ShtName As String) As Long
Dim WS As Worksheet, Res As Long
Res = 0
For Each WS In ThisWorkbook.Worksheets
If Left(WS.Name, 3) = ShtName Then
Res = Res + 1
End If
Next
CountSheetsWithName = Res
End Function
Update your DoHL7Parsing subroutine as follows:
Sub DoHL7Parsing(sMessage As String)
Dim vSegments As Variant, vCurSeg As Variant
Dim vFields As Variant, rCurField As Range, iIter As Integer
Dim wsSeg As Worksheet, sShtName As String
vSegments = VBA.Split(sMessage, HL7_DELIMITER_SEGMENT)
Application.ScreenUpdating = False
For Each vCurSeg In vSegments
vFields = VBA.Split(vCurSeg, HL7_DELIMITER_FIELD)
For iIter = 1 To UBound(vFields)
sShtName = vFields(0) & (CountSheetsWithName(CStr(vFields(0))) + 1) ' Append the count + 1 to end of name.
Sheets.Add(After:=Sheets(Sheets.Count)).Name = sShtName
Set rCurField = ThisWorkbook.Worksheets(sShtName).Range("A" & Rows.Count).End(xlUp).Offset(1, 0)
rCurField.Value = vFields(0)
rCurField.Offset(0, 1).Value = (rCurField.Row - 1)
rCurField.Offset(0, 2).NumberFormat = "#"
rCurField.Offset(0, 2).Value = vFields(iIter)
Next iIter
Next vCurSeg
Application.ScreenUpdating = True
End Sub
Result:
Hope this helps.
Related
I've been working on the code below for a while now and I'm almost done. It's taking 3 cells of data from one sheet, copying it in another, saving a copy based on the name in the first sheet and then looping until completed for all filled rows.
The snag I'm hitting is that when the first loop completes and it needs to select the WB that holds the data (the selection is needed for the function) it can't select it due to a fault in WsStam.Cells(row, iKolomnrCorpID).EntireRow.Select. When I debug, switch to the WB and run code it does work.
It's probably something stupid I'm missing. I appreciate your help!
Sub motivatieFormOpmaken()
Dim wbMotivTemp As Workbook
Dim wsMotiv As Worksheet
Dim PathOnly, mot, FileOnly As String
Dim StrPadSourcenaam As String
Dim WsStam As Worksheet
Dim WbStam As Workbook
Dim LastRow As Long
Set wbMotivTemp = ThisWorkbook
Set wsMotiv = ActiveSheet
StrHoofdDocument = ActiveWorkbook.Name
StrPadHoofdDocument = ActiveWorkbook.Path
StrPadSourcenaam = StrPadHoofdDocument & "\" & c_SourceDump
If Not FileThere(StrPadSourcenaam) Then
MsgBox "Document " & StrPadSourcenaam & " is niet gevonden."
Exit Sub
End If
Application.ScreenUpdating = False
Workbooks.Open FileName:=StrPadSourcenaam
Set WbStam = ActiveWorkbook
Set WsStam = WbStam.Worksheets("Stambestand")
Application.Run "Stambestand.xlsm!unhiderowsandcolumns"
Worksheets("stambestand").Activate
iLaatsteKolom = Worksheets("stambestand").Cells.SpecialCells(xlLastCell).Column
iLaatsteRij = Worksheets("stambestand").Cells.SpecialCells(xlLastCell).row
VulKolomNr
If KolomControle = False Then Exit Sub
Cells(1, iKolomnrVerwijderen_uit_de_tellingen).AutoFilter Field:=iKolomnrVerwijderen_uit_de_tellingen, Criteria1:="0"
LastRow = Cells(1, iKolomnrCorpID).End(xlDown).row
Dim row As Long
row = 2
With WsStam
Do Until row > iLaatsteRij
If .Cells(row, iKolomnrCorpID).RowHeight > 0 Then
WsStam.Cells(row, iKolomnrCorpID).EntireRow.Select 'It crashes at this line, after the first loop
wsMotiv.Range("motiv_cid") = Cells(row, iKolomnrCorpID).Text
wsMotiv.Range("motiv_naam") = Cells(row, iKolomnrNaam).Text
wsMotiv.Range("motiv_ldg") = Cells(row, iKolomnrHuidigeLeidingGevende).Text
n = naamOpmaken
wbMotivTemp.Activate
ActiveWorkbook.SaveAs FileName:=StrPadHoofdDocument & "\Docs\" & n & ".xlsm", FileFormat:=xlOpenXMLWorkbookMacroEnabled, CreateBackup:=False
End If
row = row + 1
Loop
End With
End Sub
Function naamOpmaken() As String
Dim rng As Range
Dim row As Range
Set rng = Selection.SpecialCells(xlCellTypeVisible)
iRijnummer = rng.row
If iRijnummer > 1 Then
naam = Cells(iRijnummer, iKolomnrNaam).Text
ldg = Cells(iRijnummer, iKolomnrHuidigeLeidingGevende).Text
cid = Cells(iRijnummer, iKolomnrCorpID).Text
Dim Position As Long, Length As Long
Dim n As String
Position = InStrRev(naam, " ")
Length = Len(naam)
n = Right(naam, Length - Position)
End If
naamOpmaken = n + "-" + ldg + "-" + cid
End Function
you have to activate a worksheet before selecting a cell of
since you're jumping between sheets you have to add
WsStam.Activate
right before
WsStam.Cells(row, iKolomnrCorpID).EntireRow.Select
BTW, you don't seem to need that selection at all so you may want to try and comment that line!
Hopefully you may find this useful for the future.
I've had a look through your code and made some updates so you shouldn't have to select any sheets and that problem line is removed completely. I've also added a new function at the bottom which will find the last cell on any sheet you reference.
Option Explicit 'Very important at top of module.
'Ensures all variables are declared correctly.
Sub motivatieFormOpmaken()
Dim wbMotivTemp As Workbook
Dim wsMotiv As Worksheet
' Dim PathOnly, mot, FileOnly As String
'''''''''''''''''''
'New code.
Dim PathOnly As String, mot As String, FileOnly As String
'''''''''''''''''''
Dim StrPadSourcenaam As String
'''''''''''''''''''
'New code.
Dim StrHoofdDocument As String
Dim StrPadHoofdDocument As String
Dim c_SourceDump As String
c_SourceDump = "MyFileName.xlsx"
Dim KolomControle As Boolean
'''''''''''''''''''
Dim WsStam As Worksheet
Dim WbStam As Workbook
Dim LastRow As Long
Set wbMotivTemp = ThisWorkbook
Set wsMotiv = ActiveSheet
StrHoofdDocument = ActiveWorkbook.Name
StrPadHoofdDocument = ActiveWorkbook.Path
StrPadSourcenaam = StrPadHoofdDocument & "\" & c_SourceDump
If Not FileThere(StrPadSourcenaam) Then
MsgBox "Document " & StrPadSourcenaam & " is niet gevonden."
Else
' Exit Sub
' End If
Application.ScreenUpdating = False
' Workbooks.Open Filename:=StrPadSourcenaam
' Set WbStam = ActiveWorkbook
'''''''''''''''''''
'New code.
Set WbStam = Workbooks.Open(Filename:=StrPadSourcenaam)
'''''''''''''''''''
Set WsStam = WbStam.Worksheets("Stambestand")
' Application.Run "Stambestand.xlsm!unhiderowsandcolumns"
'''''''''''''''''''
'New code as possible replacement for "unhiderowsandcolumns"
WsStam.Cells.EntireColumn.Hidden = False
WsStam.Cells.EntireRow.Hidden = False
'''''''''''''''''''
' Worksheets("stambestand").Activate
' iLaatsteKolom = Worksheets("stambestand").Cells.SpecialCells(xlLastCell).Column
' iLaatsteRij = Worksheets("stambestand").Cells.SpecialCells(xlLastCell).row
'''''''''''''''''''
'New code. You may want to check for filters before finding last row?
iLaatsteKolom = LastCell(WsStam).Column
iLaatsteRij = LastCell(WsStam).row
'''''''''''''''''''
VulKolomNr 'No idea - getting deja vu here.
' If KolomControle = False Then Exit Sub
'''''''''''''''''''
'New code.
If KolomControle Then
'''''''''''''''''''
WsStam.Cells(1, iKolomnrVerwijderen_uit_de_tellingen).AutoFilter Field:=iKolomnrVerwijderen_uit_de_tellingen, Criteria1:="0"
' LastRow = Cells(1, iKolomnrCorpID).End(xlDown).row
'''''''''''''''''''
'New code. The function will return the last filtered row.
LastRow = LastCell(WsStam).row
'''''''''''''''''''
Dim row As Long
row = 2
With WsStam
Do Until row > iLaatsteRij
If .Cells(row, iKolomnrCorpID).RowHeight > 0 Then
'''''''''''''''''''
'I don't think you even need this line.
' WsStam.Cells(row, iKolomnrCorpID).EntireRow.Select 'It crashes at this line, after the first loop
' wsMotiv.Range("motiv_cid") = Cells(row, iKolomnrCorpID).Text
' wsMotiv.Range("motiv_naam") = Cells(row, iKolomnrNaam).Text
' wsMotiv.Range("motiv_ldg") = Cells(row, iKolomnrHuidigeLeidingGevende).Text
'''''''''''''''''''
'New code. Note the "." before "Cells" which tells it that cell is on "WsStam" (in the "With")
' Also formatting the cell to text - will need to update as required.
wsMotiv.Range("motiv_cid") = Format(.Cells(row, iKolomnrCorpID), "0000")
wsMotiv.Range("motiv_naam") = Format(.Cells(row, iKolomnrNaam), "0000")
wsMotiv.Range("motiv_ldg") = Format(.Cells(row, iKolomnrHuidigeLeidingGevende), "0000")
'Do you mean this to save on each loop?
' n = naamOpmaken
' wbMotivTemp.Activate
' ActiveWorkbook.SaveAs Filename:=StrPadHoofdDocument & "\Docs\" & n & ".xlsm", FileFormat:=xlOpenXMLWorkbookMacroEnabled, CreateBackup:=False
'''''''''''''''''''
'New code. Combines the above three lines.
wbMotivTemp.SaveAs Filename:=StrPadHoofdDocument & "\Docs\" & naamOpmaken(WsStam) & ".xlsm", FileFormat:=xlOpenXMLWorkbookMacroEnabled, CreateBackup:=False
End If
row = row + 1
Loop
End With
'''''''''''''''''''
'New code. End of "If KolomControle" block.
End If
'''''''''''''''''''
''''''''''''''''
'New code - end of "If Not FileThere" block.
'Give procedure a single exit point.
End If
End Sub
'Added the worksheet as an argument to the procedure.
'This is then passed from the main procedure and you don't need to select the sheet first.
Function naamOpmaken(wrkSht As Worksheet) As String
Dim rng As Range
Dim row As Range
Set rng = Selection.SpecialCells(xlCellTypeVisible)
'''''''''''''''''''
'New code
Dim naam As String
Dim ldg As String
Dim cid As String
'''''''''''''''''''
iRijnummer = rng.row
If iRijnummer > 1 Then
' naam = Cells(iRijnummer, iKolomnrNaam).Text
' ldg = Cells(iRijnummer, iKolomnrHuidigeLeidingGevende).Text
' cid = Cells(iRijnummer, iKolomnrCorpID).Text
'''''''''''''''''''
'New code - not reference to the worksheet, and using default value of cell.
' may need to add "FORMAT" to get numericals in correct format.
naam = wrkSht.Cells(iRijnummer, iKolomnrNaam)
ldg = wrkSht.Cells(iRijnummer, iKolomnrHuidigeLeidingGevende)
cid = wrkSht.Cells(iRijnummer, iKolomnrCorpID)
'''''''''''''''''''
Dim Position As Long, Length As Long
Dim n As String
Position = InStrRev(naam, " ")
Length = Len(naam)
n = Right(naam, Length - Position)
End If
'If n and ldg are numbers this will add them rather than stick them together.
' naamOpmaken = n + "-" + ldg + "-" + cid
''''''''''''''''
'New code
naamOpmaken = n & "-" & ldg & "-" & cid
''''''''''''''''
End Function
'New function to find last cell containing data on sheet.
Public Function LastCell(wrkSht As Worksheet) As Range
Dim lLastCol As Long, lLastRow As Long
On Error Resume Next
With wrkSht
lLastCol = .Cells.Find("*", , , , xlByColumns, xlPrevious).Column
lLastRow = .Cells.Find("*", , , , xlByRows, xlPrevious).row
If lLastCol = 0 Then lLastCol = 1
If lLastRow = 0 Then lLastRow = 1
Set LastCell = wrkSht.Cells(lLastRow, lLastCol)
End With
On Error GoTo 0
End Function
I have a list with 3 variables in the sheet "Combined" in columns A; B; C.
The workbook contains 98 sheets, with those 3 variables still in A; B; C columns but in different combinations and with a fourth column which never repeats itself, as the sheets go on, which i need to bring in the "Combined" sheet, always adding another column for the next sheet I vlookup. : A B C + D(from the next sheet) + E(from the next sheet) and so on.
I have a UDF that Vlookups on 3 based on 3 criterias and a macro that cycles through the sheets and bring the values where i want them. The problem is, it's pretty slow, left it from yesterday and its on sheet 60. Any suggestions on improving it would greatly help, Thank you in advance!
Function ThreeVlookup(Table_Range As Range, Return_Col As Long, Col1_Fnd, Col2_Fnd, Col3_Fnd)
Dim rCheck As Range, bFound As Boolean, lLoop As Long
On Error Resume Next
Set rCheck = Table_Range.Columns(1).Cells(1, 1)
With WorksheetFunction
For lLoop = 1 To .CountIf(Table_Range.Columns(1), Col1_Fnd)
Set rCheck = Table_Range.Columns(1).Find(Col1_Fnd, rCheck, xlValues, xlWhole, xlNext, xlRows, False)
If UCase(rCheck(1, 2)) = UCase(Col2_Fnd) And UCase(rCheck(1, 3)) = UCase(Col3_Fnd) Then
bFound = True
Exit For
End If
Next lLoop
End With
If bFound = True Then
ThreeVlookup = rCheck(1, Return_Col)
Else
ThreeVlookup = ""
End If
End Function
Sub test()
Dim lookupVal1 As Range, lookupVal2 As Range, lookupVal3 As Range, myString As Variant, n&, u As Long
n = Sheets("Combined").[A:A].Cells.Find("*", , , , xlByRows, xlPrevious).Row
u = 4
For j = 2 To Worksheets.Count
For i = 1 To n
Set lookupVal1 = Sheets("Combined").Cells(i, 1)
Set lookupVal2 = Sheets("Combined").Cells(i, 2)
Set lookupVal3 = Sheets("Combined").Cells(i, 3)
myString = ThreeVlookup(Sheets(j).Range("A:D"), 4, lookupVal1, lookupVal2, lookupVal3)
Sheets("Combined").Cells(i, u) = myString
Next i
u = u + 1
Next j
End Sub
Use Arrays to speed it up, my friend! Load all your sheets (or just the current sheet in the loop) into an array in VBA's memory and do the .CountIf and .Find on arrayVar(row) instead of Table_Range.Columns(1).
You will be really surprised how much quicker it goes. Do it!
Here's a tutorial I like on arrays...
http://www.cpearson.com/excel/ArraysAndRanges.aspx
Here's a guy who speed-tested an application like yours...
https://fastexcel.wordpress.com/2011/10/26/match-vs-find-vs-variant-array-vba-performance-shootout/
The basics is like this:
Sub Play_With_Arrays()
Dim varArray() As Variant
Dim lngArray() As Long
ReDim varArray(1 To 1000)
ReDim lngArray(1 To 1000)
For A = 1 To 1000
lngArray(A) = A / 2
varArray(A) = A / 2 & " examples"
Next
searchterm = 345
For B = 1 To 1000
If lngArray(B) = searchterm Then
FoundRow = B
End If
Next
searchterm2 = "5 ex"
FoundStrRowCount = 0
For C = 1 To 1000
If InStr(1, varArray(C), searchterm2, vbBinaryCompare) Then
FoundStrRowCount = FoundStrRowCount + 1
End If
Next
MsgBox (FoundRow & " in long array and " & FoundStrRowCount & " in var array")
End Sub
Something like this should be much faster:
Public Function ThreeVLookup(ByVal arg_Col1LookupVal As Variant, _
ByVal arg_Col2LookupVal As Variant, _
ByVal arg_Col3LookupVal As Variant, _
ByVal arg_LookupTable As Range, _
ByVal arg_ReturnColumn As Long) _
As Variant
Dim rConstants As Range, rFormulas As Range
Dim rAdjustedTable As Range
Dim aTable As Variant
Dim i As Long
On Error Resume Next
Set rConstants = arg_LookupTable.SpecialCells(xlCellTypeConstants)
Set rFormulas = arg_LookupTable.SpecialCells(xlCellTypeFormulas)
On Error GoTo 0
Select Case (Not rConstants Is Nothing) + 2 * (Not rFormulas Is Nothing)
Case 0: ThreeVLookup = vbNullString
Exit Function
Case -1: Set rAdjustedTable = rConstants
Case -2: Set rAdjustedTable = rFormulas
Case -3: Set rAdjustedTable = Union(rConstants, rFormulas)
End Select
If WorksheetFunction.CountIfs(rAdjustedTable.Resize(, 1), arg_Col1LookupVal, rAdjustedTable.Resize(, 1).Offset(, 1), arg_Col2LookupVal, rAdjustedTable.Resize(, 1).Offset(, 2), arg_Col3LookupVal) = 0 Then
ThreeVLookup = vbNullString
Exit Function
End If
aTable = rAdjustedTable.Value
For i = LBound(aTable, 1) To UBound(aTable, 1)
If aTable(i, 1) = arg_Col1LookupVal And aTable(i, 2) = arg_Col2LookupVal And aTable(i, 3) = arg_Col3LookupVal Then
ThreeVLookup = aTable(i, arg_ReturnColumn)
Exit Function
End If
Next i
End Function
Sub tgr()
Dim wb As Workbook
Dim wsCombined As Worksheet
Dim ws As Worksheet
Dim aResults() As Variant
Dim aCombined As Variant
Dim i As Long, j As Long
Set wb = ActiveWorkbook
Set wsCombined = wb.Sheets("Combined")
aCombined = wsCombined.Range("A1").CurrentRegion.Value
ReDim aResults(1 To UBound(aCombined, 1) - LBound(aCombined, 1) + 1, 1 To wb.Sheets.Count - 1)
For i = LBound(aCombined, 1) To UBound(aCombined, 1)
j = 0
For Each ws In wb.Sheets
If ws.Name <> wsCombined.Name Then
j = j + 1
aResults(i, j) = ThreeVLookup(aCombined(i, 1), aCombined(i, 2), aCombined(i, 3), ws.Range("A:D"), 4)
End If
Next ws
Next i
wsCombined.Range("D1").Resize(UBound(aResults, 1), UBound(aResults, 2)).Value = aResults
End Sub
I am generating a new workbook from a multiple workbooks, i can generate a summary of all the errors found, but when i try to copy the sheets with the error information i got the runtime error 9
These is the line failing
If exists = True Then
ActiveWorkbook.Sheets(sheetName).Copy After:=ThisWorkbook.Sheets(1)
End If
Other thing i havent add is that all the sheets on the multiple files have the same names, so i want to know if there is a way that the sheet when is copy i can add the file name and the sheet name
Sub getViolations()
Path = "C:\Users\omartinr\Desktop\New folder (4)\New folder\"
Filename = Dir(Path & "*.xls")
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
Set TxtRng = ws.Range("A1:N1")
TxtRng.Font.ColorIndex = 2
TxtRng.Interior.ColorIndex = 5
TxtRng.Value = [{"Partition Name","Tag","EM Supply","SH Signal","PK","Sfactor","FiSH","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL"}]
TxtRng.HorizontalAlignment = xlCenter
Dim i As Integer
i = 2
Do While Filename <> ""
Workbooks.Open Filename:=Path & Filename, ReadOnly:=True
Dim wc As Worksheet
Set wc = ActiveWorkbook.Sheets("Violations Summary")
ws.Cells(i, 1).Value = ActiveWorkbook.Sheets("Violations Summary").Range("B1")
ws.Cells(i, 2).Value = ActiveWorkbook.Sheets("Violations Summary").Range("C1")
Dim count As Integer
count = 15
Dim sheetName As String, mySheetNameTest As String
Dim n As Integer
Dim exits As Boolean
For n = 3 To 14
If Not IsEmpty(wc.Cells(n, 2)) Then
If (wc.Cells(n, 2)) = 0 Then
ws.Cells(i, n).Font.ColorIndex = 4
ws.Cells(i, n).Value = wc.Cells(n, 2)
End If
If (wc.Cells(n, 2)) > 0 Then
Select Case wc.Cells(n, 1)
Case "PK"
sheetName = "Peak"
Case "Sfactor"
sheetName = "SF Supply"
Case Else
sheetName = wc.Cells(n, 1)
End Select
exists = sheetExists(sheetName)
If exists = True Then
ActiveWorkbook.Sheets(sheetName).Copy After:=ThisWorkbook.Sheets(1)
End If
ws.Cells(i, count) = wc.Cells(1, n).Value
ws.Cells(i, n).Font.ColorIndex = 3
ws.Cells(i, n).Value = wc.Cells(n, 2)
End If
If (ActiveWorkbook.Sheets("Violations Summary").Cells(n, 2)) < 0 Then
ws.Cells(i, n).Font.ColorIndex = 3
ws.Cells(i, n).Value = wc.Cells(n, 2)
End If
End If
If IsEmpty(wc.Cells(n, 2)) Then
ws.Cells(i, n).Value = ["NA"]
End If
count = count + 1
Next n
Workbooks(Filename).Close
Filename = Dir()
i = i + 1
Loop
End Sub
Function sheetExists(sheetToFind As String) As Boolean
sheetExists = False
For Each Sheet In Worksheets
If sheetToFind = Sheet.Name Then
sheetExists = True
Exit Function
End If
Next Sheet
End Function
Put option explicit at top so spelling of variables is checked and that they are declared. The variable exists was mispelt and there were a number of other variables not declared. I have put some other comments in with the code.
Some of the logic i think can be simplified and i have given some examples. Also, ensure consistent use of named variable wc. If nothing else it should be easier to debug now. Compiles on my machine so give it a try.
This all works on the assumption that each workbook you open has the "Violations Summary" sheet and it is spelt as shown.
You have the filename already stored in the variable Filename so you can use (concatenate?) that with the sheetname variable.
Option Explicit 'Set this to ensure all variable declared and consistent spelling
'Consider using WorkSheets collection rather than Sheets unless you have chart sheets as well?
Sub getViolations()
Dim Path As String 'Declare you other variables
Dim FileName As String
Path = "C:\Users\omartinr\Desktop\New folder (4)\New folder\"
FileName = Dir(Path & "*.xls")
Dim ws As Worksheet
Dim TxtRng As Range 'Declare this
Set ws = ThisWorkbook.Sheets("Sheet1")
Set TxtRng = ws.Range("A1:N1")
TxtRng.Font.ColorIndex = 2
TxtRng.Interior.ColorIndex = 5
TxtRng.Value = [{"Partition Name","Tag","EM Supply","SH Signal","PK","Sfactor","FiSH","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL"}]
TxtRng.HorizontalAlignment = xlCenter
Dim i As Integer
i = 2
Do While FileName <> ""
Workbooks.Open FileName:=Path & FileName, ReadOnly:=True
Dim wc As Worksheet 'Consider whether to place these declarations just before the loop, avoids risk others may think there will be reinitialization even though there isn't
Set wc = ActiveWorkbook.Sheets("Violations Summary")
ws.Cells(i, 1).Value = wc.Range("B1") 'Use the wc variable
ws.Cells(i, 2).Value = wc.Range("C1")
Dim count As Integer
Dim sheetName As String, mySheetNameTest As String
Dim n As Integer
Dim exists As Boolean 'Corrected spelling
count = 15
For n = 3 To 14
If Not IsEmpty(wc.Cells(n, 2)) Then
If (wc.Cells(n, 2)) = 0 Then
ws.Cells(i, n).Font.ColorIndex = 4
ws.Cells(i, n).Value = wc.Cells(n, 2)
End If
If (wc.Cells(n, 2)) > 0 Then
Select Case wc.Cells(n, 1)
Case "PK"
sheetName = "Peak"
Case "Sfactor"
sheetName = "SF Supply"
Case Else
sheetName = wc.Cells(n, 1)
End Select
exists = sheetExists(sheetName)
If exists Then 'Shortened by removing = True (evaluates in same way)
ActiveWorkbook.Sheets(sheetName).Copy After:=ThisWorkbook.Sheets(1)
End If
ws.Cells(i, count) = wc.Cells(1, n).Value
ws.Cells(i, n).Font.ColorIndex = 3
ws.Cells(i, n).Value = wc.Cells(n, 2)
End If
If (wc.Cells(n, 2)) < 0 Then 'used wc variable
ws.Cells(i, n).Font.ColorIndex = 3
ws.Cells(i, n).Value = wc.Cells(n, 2)
End If
Else 'Simplified this as if is not empty then is empty so can use else
ws.Cells(i, n).Value = ["NA"] 'what is pupose of square brackets? These can be removed i think
End If
count = count + 1
Next n
Workbooks(FileName).Close
FileName = Dir()
i = i + 1
Loop
End Sub
Function sheetExists(sheetToFind As String) As Boolean
Dim Sheet As Worksheet ' declare
sheetExists = False
For Each Sheet In Worksheets
If sheetToFind = Sheet.Name Then
sheetExists = True
Exit Function
End If
Next Sheet
End Function
After you copy the ActiveWorkbook.Sheets(sheetName) to ThisWorkbook, ThisWorkbook becomes the ActiveWorkbook. ActiveWorkbook.Sheets(sheetName).Copy After:=ThisWorkbook.Sheets(1) should not throw an error but will probably cause ActiveWorkbook.Sheets("Violations Summary") to fail. For this reason, you should always fully qualify your references.
Some idealist programmers say that a subroutine should perform 1 simply task. Personally, I believe that if you have to scroll up, down, left or right to see what your code is doing it is time to refactor it. When refactoring I try to extract logical groups of tasks in a separate subroutine. This makes debugging and modifying the code far easier.
Refactored Code
Option Explicit
Sub getViolations()
Const Path As String = "C:\Users\omartinr\Desktop\New folder (4)\New folder\"
Dim n As Long
Dim Filename As String
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
Sheet1Setup ws
Filename = Dir(Path & "*.xls")
Do While Filename <> ""
ProcessWorkbook Filename, ws.Rows(n)
Filename = Dir()
Loop
End Sub
Sub ProcessWorkbook(WBName As String, row As Range)
Dim nOffset As Long, n As Long
Dim sheetName As String
Dim WB As Workbook
Set WB = Workbooks.Open(Filename:=Path & Filename, ReadOnly:=True)
With WB.Sheets("Violations Summary")
row.Columns(1).Value = .Range("B1")
row.Columns(2).Value = .Range("C1")
nOffset = 12
For n = 3 To 14
If .Cells(n, 2) = "" Then
row.Columns(n).Value = ["NA"]
ElseIf (.Cells(n, 2)) = 0 Then
row.Columns(n).Font.ColorIndex = 4
row.Columns(n).Font.ColorIndex = 0
ElseIf (.Cells(n, 2)) = 0 Then
Select Case wc.Cells(n, 1)
Case "PK"
sheetName = "Peak"
Case "Sfactor"
sheetName = "SF Supply"
Case Else
sheetName = wc.Cells(n, 1)
End Select
'Range.Parent refers to the ranges worksheet. row.Parent refers to ThisWorkbook.Sheets(1)
If SheetExists(WB, sheetName) Then .Copy After:=row.Parent.Sheets(1)
row.Columns(n + nOffset) = .Cells(1, n).Value
row.Columns(n).Font.ColorIndex = 3
row.Columns(n).Value = .Cells(n, 2)
End If
Next
End With
WB.Close SaveChanges:=False
End Sub
Function SheetExists(WB As Workbook, sheetToFind As String) As Boolean
Dim ws As Worksheet
For Each ws In WB.Worksheets
If sheetToFind = ws.Name Then
SheetExists = True
Exit Function
End If
Next
End Function
Sub Sheet1Setup(ws As Worksheet)
With ws.Range("A1:N1")
.Value = [{"Partition Name","Tag","EM Supply","SH Signal","PK","Sfactor","FiSH","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL"}]
.Font.ColorIndex = 2
.Interior.ColorIndex = 5
.HorizontalAlignment = xlCenter
End With
End Sub
Note: row is the target Row of ThisWorkbook.Sheets(1). row.Columns(3) is a fancy way to write row.Cells(1, 3) which refers to the 3rd cell in the target row. Also note that Cells, Columns, and Rows are all relative to the range they belong to. e.g. Range("C1").Columns(2) refers to D1, Range("C1").Rows(2).Columns(2) refers to D2, Range("C1").Cells(2,2) also refers to D2.
Basically, have this code which uses Vlookups and a match to find past order dates of a particular product. The sub fills text boxes in a userform with N/A if there are no past orders found in the sheet. Otherwise, finds the latest order and fills the information in the userform.
The program stops when this sub routine is called. Getting 'subscript out of range' (error 9), I run the debugger and go through the code and everything works the way it is supposed to. In both the N/A case and the case where there is past order info.
Sub PastOrderInfo()
Dim wks As Worksheet
Dim Date_Ordered As Variant
Dim PreviousDate As Variant
Dim Qty_Ordered As String
Dim Total_Cost As String
Dim Rng, RngCol As String
Dim Last_Row As Long
Dim i, NewRow As Integer
Set wks = Worksheets("Order Data")
With wks
Last_Row = .UsedRange.Rows(.UsedRange.Rows.count).Row
Rng = "A2:D" & Last_Row
RngCol = "A2:A" & Last_Row
For i = 2 To Last_Row
If i = 2 Then
On Error Resume Next
PreviousDate = Application.VLookup(CStr(ProdNum), .Range(Rng), 2, False)
On Error GoTo 0
If IsError(PreviousDate) Then
Me.TextBox4.Value = "N/A"
Me.TextBox5.Value = "N/A"
Me.TextBox6.Value = "N/A"
Exit Sub
End If
NewRow = Application.Match(CStr(ProdNum), .Range(RngCol), 0) + 2
Rng = "A" & NewRow & ":D" & Last_Row
RngCol = "A" & NewRow & ":A" & Last_Row
ElseIf i > 2 Then
On Error Resume Next
Date_Ordered = Application.VLookup(CStr(ProdNum), .Range(Rng), 2, False)
On Error GoTo 0
If IsError(Date_Ordered) Then
NewRow = NewRow - 1
Rng = "A" & NewRow & ":D" & Last_Row
Me.TextBox4.Value = CDate(PreviousDate)
Me.TextBox5.Value = Application.VLookup(CStr(ProdNum), .Range(Rng), 3, False)
Me.TextBox6.Value = Application.VLookup(CStr(ProdNum), .Range(Rng), 4, False)
Exit Sub
End If
NewRow = Application.Match(CStr(ProdNum), .Range(RngCol), 0) + NewRow
Rng = "A" & NewRow & ":D" & Last_Row
RngCol = "A" & NewRow & ":A" & Last_Row
If Date_Ordered > PreviousDate Then PreviousDate = Date_Ordered
End If
Next i
Me.TextBox4.Value = CDate(PreviousDate)
Me.TextBox5.Value = Application.VLookup(CStr(ProdNum), .Range(Rng), 3, False)
Me.TextBox6.Value = Application.VLookup(CStr(ProdNum), .Range(Rng), 4, False)
End With
End Sub
Here is the line which is the section of code which opens the userform, when I click to debug it highlights the ProDescription.Show line below the if .Range(cellselect)...:
Private Sub CommandButton1_Click()
Dim i, r, c As Integer
Dim wks As Worksheet
Dim cellselect As String
Set wks = Workbooks("Data Direct Orders2.xlsx").Worksheets("Direct Items")
With wks
If ProdNumberCmbBox.ListIndex = -1 Then
Unload Me
ErrorMsg.Show
End
Else
For r = 2 To 84
cellselect = "A" & r
If .Range(cellselect).Text = ProdNum Then
ProDescription.Show
Unload Me
End
End If
Next r
If c = 0 Then
Unload Me
ErrorMsg.Show
End
End If
End If
End With
End Sub
Here is the sub routine where the userform is initialized:
Private Sub UserForm_Initialize()
TextBox8.Value = ProdNum
Call PastOrderInfo
End Sub
Just figured it out.
The line:
Set wks = Worksheets("Order Data")
in
Sub PastOrderInfo()
Was the problem. Needed to specify the workbook, so after adding:
Set wks = Workbooks("VBA - Final Project.xlsm").Worksheets("Order Data")
It worked!
I'm trying to copy a sheet by its code name and rename the copied sheets display name and code name,
I've come up with this but it only work one time and then it gets an error because there is already a sheet with that display name and codename, is there a why i can just add value + 1 to the end of the names?
Sub TESTONE()
Dim MySheetName As String
MySheetName = "Rename Me"
VBA_Copy_Sheet.Copy After:=ActiveSheet
ActiveSheet.Name = MySheetName
ActiveSheet.Tab.ColorIndex = 3
Dim wks As Worksheet
Set wks = ActiveSheet
ThisWorkbook.VBProject.VBComponents(wks.CodeName).Name = "BidSheet"
End Sub
I wish, it helps to you
Sub TESTONE()
Dim MySheetName As String
Dim MyCodeName As String
Dim wks As Worksheet
MySheetName = "Rename Me"
MyCodeName = "BidSheet"
If VBA_Copy_Sheet = Empty Then
Set VBA_Copy_Sheet = ActiveSheet
End If
VBA_Copy_Sheet.Copy After:=ActiveSheet
ActiveSheet.Name = GetNewSheetName(MySheetName, 0)
ActiveSheet.Tab.ColorIndex = 3
Set wks = ActiveSheet
MyCodeName = GetNewCodeName(MyCodeName, 0)
ThisWorkbook.VBProject.VBComponents(wks.CodeName).Name = MyCodeName
End Sub
Function GetNewSheetName(ByVal newName As String, ByVal n As Integer) As String
Dim ws As Worksheet
Dim modifiedName As String
modifiedName = newName & n
For Each ws In ActiveWorkbook.Worksheets
If ws.Name = modifiedName Then
n = n + 1
modifiedName = GetNewSheetName(newName, n)
Exit For
End If
Next
GetNewSheetName = modifiedName
End Function
Function GetNewCodeName(ByVal newName As String, ByVal n As Integer) As String
Dim ws As Worksheet
Dim modifiedName As String
modifiedName = newName & n
For Each ws In ActiveWorkbook.Worksheets
If ws.CodeName = modifiedName Then
n = n + 1
modifiedName = GetNewCodeName(newName, n)
Exit For
End If
Next
GetNewCodeName = modifiedName
End Function
You could store a counter in a range name and use that to increment your sheet, i.e:
Dim strName As String
Dim strCnt As String
Dim MySheetName As String
strName = "SheetCnt"
On Error Resume Next
strCnt = ActiveWorkbook.Names(strName).Value
On Error GoTo 0
If Len(strCnt) = 0 Then
ActiveWorkbook.Names.Add strName, 1
Else
strCnt = Replace(strCnt, "=", Chr(32)) + 1
ActiveWorkbook.Names(strName).RefersTo = strCnt
End If
MySheetName = "Rename Me " & strCnt