I have a script that maps out data and prints an array to a template based on when values in a column change (when the next set of duplicates start), basically stops and prints and array when cells M2<>M3 type deal.
It goes through and saves off a copy from the template for everyone but the final set of data, it just sits in the template and doesn't save off. How can I edit my code to go through all values and not leave the last set of data for me to manually save off?
Option Explicit
Sub Main()
Dim wb As Workbook
Dim Report_Data, Last, Login
Dim i As Long, j As Long, k As Long, a As Long
Dim Destination_Rng As Range
Workbooks.Open filename:="C:\Goal_Report_Template.xlsx"
Set wb = Workbooks("Goal_Report_Template.xlsx")
Set Destination_Rng = wb.Sheets("Sheet1").Range("A2")
With ThisWorkbook.Sheets("Q1 report")
Report_Data = .Range("W2", .Range("A" & Rows.Count).End(xlUp))
End With
wb.Activate
Application.ScreenUpdating = False
For i = 1 To UBound(Report_Data)
If Report_Data(i, 14) <> Last Then
If i > 1 Then
Destination_Rng.Select
wb.SaveCopyAs ThisWorkbook.Path & Application.PathSeparator & _
ValidFileName(Login & " - " & Last & " - Goal Reporting.xlsx")
End If
Rows(1).Offset(1, 0).Resize(Rows.Count - 1).EntireRow.ClearContents
Last = Report_Data(i, 14)
Login = Report_Data(i, 13)
j = 0
End If
a = 0
For k = 1 To UBound(Report_Data, 2)
Destination_Rng.Offset(j, a) = Report_Data(i, k)
a = a + 1
Next
j = j + 1
Next
End Sub
You need to perform another SaveAs after exiting the i loop. You can avoid duplicating code by breaking that out into a separate sub.
Untested:
Sub Main()
Dim wb As Workbook
Dim Report_Data, Last, Login, Current
Dim i As Long, j As Long, k As Long, a As Long
Dim Destination_Rng As Range
Set wb = Workbooks.Open(Filename:="C:\Goal_Report_Template.xlsx")
Set Destination_Rng = wb.Sheets("Sheet1").Range("A2")
With ThisWorkbook.Sheets("Q1 report")
Report_Data = .Range("W2", .Range("A" & .Rows.Count).End(xlUp))
End With
Application.ScreenUpdating = False
For i = 1 To UBound(Report_Data)
Current = Report_Data(i, 14)
If Current <> Last Then
If i > 1 Then SaveCopy wb, Login, Last '<< save this one
Destination_Rng.CurrentRegion.Offset(1, 0).ClearContents
Login = Report_Data(i, 13)
j = 0
Else
j = j + 1
End If
For k = 1 To UBound(Report_Data, 2)
Destination_Rng.Offset(j, k - 1) = Report_Data(i, k)
Next k
Next i
SaveCopy wb, Login, Last '<< save the last report
End Sub
Sub SaveCopy(wb As Workbook, Login, Last)
wb.SaveCopyAs ThisWorkbook.Path & Application.PathSeparator & _
ValidFileName(Login & " - " & Last & " - Goal Reporting.xlsx")
End Sub
Related
I'm trying to compare a main planning file (let's call it Main.xlsm) and data that is provided by our ERP system (ERP.xlsm).
I'm looking to:
1) open a window to select the source file (ERP system dump).
2) compare unique ID values from column F in both files (Sheet RAPORT in Main.xlsm and Sheet1 in ERP.xlsm) and:
If there is a match between Main.xlsm and ERP.xlsm - update values in Main with values from ERP (all data - rows A:AK)
if there is an entry in ERP but no entry in Main - add the whole row with that ID (A:AK)
if there is an entry in Main but no data in ERP - place value "0" in row "R" in the Main file
Bonus round: Every time one of the above happens, place a time/date stamp in column "AL" in the row from the unique ID it altered.
I tried the code below (original version, not altered by me). I can't figure out how to achieve all from above.
Sub import_tickets()
'run this when the active file is the main ticket list and the active sheet is the ticket list
'exported file must be open already, and the ticket list must be the active sheet
Dim exported_file As String
exported_file = "exported file.xlsx"
header_exists = True 'if exported file doesn't have a header, set this to false!
starting_row = 1
If header_exists Then starting_row = 2
Dim first_blank_row As Long
first_blank_row = Cells.SpecialCells(xlCellTypeLastCell).Offset(1, 0).Row
Dim r As Long
r = starting_row
Dim found As Range
cur_ticket_num = Workbooks(exported_file).ActiveSheet.Range("a" & r).Value
Do While Not cur_ticket_num = ""
'look for current ticket number in main file
Set found = Columns("a:a").Find(what:=cur_ticket_num, LookIn:=xlValues, lookat:=xlWhole)
If found Is Nothing Then
'add info to end of main file
write_line_from_export exported_file, r, first_blank_row
first_blank_row = first_blank_row + 1
Else
'overwrite existing line of main file
write_line_from_export exported_file, r, found.Row
End If
r = r + 1
cur_ticket_num = Workbooks(exported_file).ActiveSheet.Range("a" & r).Value
Loop
End Sub
Sub write_line_from_export(src_filename As String, src_r As Long, dest_r As Long)
For c = 1 To 24
Cells(dest_r, c).Value = Workbooks(src_filename).ActiveSheet.Cells(src_r, c).Value
Next c
End Sub
Here is an example that uses a Dictionary object to compare the ID column between the 2 sheets.
Sub import_tickets()
Dim sERPFileName As String
Dim wbERP As Workbook, wsERP As Worksheet
Dim wbMain As Workbook, wsMain As Worksheet
Dim r, startrow, lastrow As Long
Dim ID
Dim dictERP
Set dictERP = CreateObject("Scripting.Dictionary")
With Application.FileDialog(msoFileDialogFilePicker)
.AllowMultiSelect = False
.Show
sERPFileName = .SelectedItems(1)
End With
Application.ScreenUpdating = False
' process ERP workbook
Set wbERP = Workbooks.Open(sERPFileName, , xlReadOnly)
Set wsERP = wbERP.Sheets("Sheet1")
startrow = 2 ' assume has header
lastrow = wsERP.Cells(Rows.Count, "F").End(xlUp).Row
For r = startrow To lastrow
ID = wsERP.Range("F" & r).Value
If dictERP.exists(ID) Then
MsgBox "Duplicate ID (" & ID & ") found in " & sERPFileName
Else
dictERP.Add ID, r
End If
Next r
' process MAIN workbook
Set wbMain = ThisWorkbook
Set wsMain = wbMain.Sheets("RAPORT")
startrow = 2 ' assume has header
lastrow = wsMain.Cells(Rows.Count, "F").End(xlUp).Row
For r = startrow To lastrow
ID = wsMain.Range("F" & r).Value
If dictERP.exists(ID) Then
' update
wsERP.Rows(dictERP(ID)).Columns("A:AK").Copy wsMain.Range("A" & r)
wsMain.Range("L" & r) = "Updated " & Now
dictERP.Remove (ID)
Else
' set col R = 0
wsMain.Range("R" & r).Value = 0
wsMain.Range("L" & r) = "No Change " & Now
End If
Next r
' add from ERP those not matched
If dictERP.Count > 0 Then
For Each ID In dictERP.keys
r = dictERP(ID)
lastrow = lastrow + 1
wsERP.Rows(r).Columns("A:AK").Copy wsMain.Range("A" & lastrow)
wsMain.Range("L" & lastrow) = "Added " & Now
Next
End If
wbERP.Close
Application.ScreenUpdating = True
If dictERP.Count Then
MsgBox dictERP.Count & " rows added"
Else
MsgBox "Done"
End If
End Sub
I pass data into an array based on when column value <> column value. The array is formed fine, but when its about to move the array to a template, it gives me an object required error. This is brand new and was not erroring out before, what could fix this?
Getting error on this line:
Dest.Offset(j,a) = Data(i,k)
Rest of Code:
Option Explicit
Sub Main()
Dim Wb As Workbook
Dim Data, Last
Dim i As Long, j As Long, k As Long, a As Long
Dim Dest As Range
Dim BASEPATH As String
Dim template As String
template = "M:\.xlsx"
BASEPATH = "M:\"
Set Wb = Workbooks.Open(Filename:=template)
Set Dest = Wb.Sheets("").Range("A3")
With ThisWorkbook.Sheets(1)
Data = .Range("BQ3", .Range("A" & Rows.Count).End(xlUp))
End With
Wb.Activate
For i = 1 To UBound(Data)
If Data(i, 10) <> Last Then
If i > 1 Then
Dest.Select
Wb.SaveCopyAs BASEPATH & _
ValidFileName(Last & "_YE_Planning_File.xlsx")
End If
With Wb.Sheets("")
.Rows(3 & ":" & .Rows.Count).Delete
End With
Last = Data(i, 10)
j = 0
End If
a = 0
For k = 1 To UBound(Data, 2)
Dest.Offset(j, a) = Data(i, k)
a = a + 1
Next
j = j + 1
Next
End Sub
Dest gets deleted.
Set Dest = Wb.Sheets("Pay for Performance Detail").Range("A3")
...
With Wb.Sheets("Pay for Performance Detail")
.Rows(3 & ":" & .Rows.Count).Delete <~ this includes A3, so `Dest` is deleted
End With
Move the Set Dest to after you do the deletion.
Better yet, don't Delete within a loop? (or maybe just ClearContents, as apparently you already had previously)
I have a macro that threw the below error, and I have a theory why, but am having trouble finding any literature to back it up. Pages I found are typically people posting silly mistakes with incorrect variable types.
I don't think there's anything wrong with the code, I just think the nature of the task takes too long, therefore overloading the temp folder. Per TechWalla (emphasis mine):
The Runtime Error 6 occurs in the Visual Basic program. It is an overflow issue that can occur when the Visual Basic program attempts to store too much data in the temporary folders area. Runtime files help Windows translate a program's language into Windows language so the program runs faster. You can get the Runtime Error 6 message for several reasons. One reason is that you are using a backslash instead of a forward slash in one of your calculations. Other reasons include an overloaded temporary folder, outdated software or a registry error.
(Caveat: I haven't seen this explanation elsewhere and can't vouch for how reliable Techwalla is. I don't know if I'm not searching with the right keywords, but like I said, I haven't found much of anything other than code-specific forum posts.)
Is there a way to determine if this is the case? I outline below why I think this is what's causing the error, which might help, but doesn't change the question. If this is the case, is there a way to find out? And if so, is there a way to prevent it?
(I'll be running it again tonight now that I've used a registry cleaner that found 1GB, though I don't know how much was from Excel. For reference, my C: drive has 180GB free...)
EDIT: Removing code, because I'm asking not asking about that, but whether or not the temporary folder overloading could actually cause this.
EDIT2: After being swayed by the people, I am re-adding the code. And I know, it's not efficient. Thank you for the suggestions though.
EDIT3 (LAST ONE, I SWEAR): Though I realize the description above specifically mentions Visual Basic, which is not VBA, I'm keeping it in as I know Excel uses/creates temporary files, and has memory limits, which is ultimately what I'm curious about.
Sub getCBU()
Dim rowCount As Long, newRow(1 To 17) As Variant, compareRow(1 To 17) As Variant, nextFile As String, s As Long
Dim location As String, lastRow As Long, match As Boolean, startTime As Double, secondsElapsed As String
location = "C:\Users\swallin\Documents\CBU History\"
nextFile = Dir(location & "CBU*")
rowCount = 2
startTime = Timer
Do While nextFile <> ""
Workbooks.Open (location & nextFile)
lastRow = Workbooks(nextFile).Worksheets(1).Cells(Rows.Count, 1).End(xlUp).Row
For s = 18 To lastRow
match = True
For x = 1 To 17
newRow(x) = Workbooks(nextFile).Worksheets(1).Cells(s, x)
Next x
For y = 2 To rowCount
If Val(newRow(11)) = Val(ThisWorkbook.Worksheets(1).Cells(y, 11)) Then
For j = 1 To 17
compareRow(j) = ThisWorkbook.Worksheets(1).Cells(y, j).Value
Next j
For v = 1 To 17
If Val(compareRow(v)) <> Val(newRow(v)) Then
match = False
Exit For
Else
match = True
End If
Next v
If match = True Then
Exit For
End If
Else
match = False
End If
Next y
y = 2
If match = False Then
rowCount = rowCount + 1
For t = 1 To 17
ThisWorkbook.Worksheets(1).Cells(rowCount, t) = newRow(t)
Next t
End If
Next s
s = 18
Workbooks(nextFile).Close
nextFile = Dir()
Loop
secondsElapsed = Format((Timer - startTime) / 86400, "hh:mm:ss")
ThisWorkbook.Worksheets(2).Cells(1, 1) = secondsElapsed
End Sub
This opens a new instance for each file and closes it afterwards. Give it a try (I could not test it). This includes all the suggestions I made in the chat.
Option Explicit
Sub getCBU()
Dim location As String
location = "C:\Users\swallin\Documents\CBU History\"
Dim nextFile As String
nextFile = Dir(location & "CBU*")
Dim rowCount As Long
rowCount = 2
Dim startTime As Double
startTime = Timer
Dim newRow(1 To 17) As Variant, compareRow(1 To 17) As Variant
Dim lastRow As Long, match As Boolean
Dim s As Long, x As Long, y As Long, j As Long, v As Long, t As Long
Dim objExcel As Object, ActWb As Workbook
Do While nextFile <> ""
Set objExcel = CreateObject("Excel.Application") 'new excel instance
Set ActWb = objExcel.Workbooks.Open(Filename:=location & nextFile, ReadOnly:=True)
lastRow = ActWb.Worksheets(1).Cells(Rows.Count, 1).End(xlUp).Row
For s = 18 To lastRow
match = True
For x = 1 To 17
newRow(x) = ActWb.Worksheets(1).Cells(s, x)
Next x
For y = 2 To rowCount
If Val(newRow(11)) = Val(ThisWorkbook.Worksheets(1).Cells(y, 11)) Then
For j = 1 To 17
compareRow(j) = ThisWorkbook.Worksheets(1).Cells(y, j).Value
Next j
For v = 1 To 17
If Val(compareRow(v)) <> Val(newRow(v)) Then
match = False
Exit For
Else
match = True
End If
Next v
If match = True Then
Exit For
End If
Else
match = False
End If
Next y
y = 2
If match = False Then
rowCount = rowCount + 1
For t = 1 To 17
ThisWorkbook.Worksheets(1).Cells(rowCount, t) = newRow(t)
Next t
End If
Next s
s = 18
ActWb.Close SaveChanges:=False
objExcel.Quit 'close excel instance
Set objExcel = Nothing 'free variable
nextFile = Dir()
Loop
Dim secondsElapsed As String
secondsElapsed = Format$((Timer - startTime) / 86400, "hh:mm:ss")
ThisWorkbook.Worksheets(2).Cells(1, 1) = secondsElapsed
End Sub
Not sure about the writing back to the sheet part (i would still allocate the values to an array and write it back all together, but that depends on what you have in the sheet already, plus whatever newRow() does), but can you give this a try and see if there is any improvement in speed?
Sub getCBU()
Dim rowCount As Long, newRow(1 To 17) As Variant, compareRow(1 To 17) As Variant, nextFile As String
Dim location As String, lastRow As Long, match As Boolean, startTime As Double, secondsElapsed As String
Dim arrData, arrOutput()
Dim arrTemp(): ReDim arrOutput(1 To 17, 1 To 1)
Dim R As Long, C As Long
location = "C:\Users\swallin\Documents\CBU History\"
nextFile = Dir(location & "CBU*")
rowCount = 2
startTime = Timer
Do While nextFile <> ""
Workbooks.Open (location & nextFile)
lastRow = Workbooks(nextFile).Worksheets(1).Cells(Rows.Count, 1).End(xlUp).row
With Workbooks(nextFile).Worksheets(1)
arrData = .Range(.Cells(1, 1), .Cells(lastRow, 17))
End With
For s = 18 To lastRow
match = True
For X = 1 To 17
newRow(X) = arrData(s, X)
Next X
For y = 2 To rowCount
If Val(newRow(11)) = Val(arrData(y, 11)) Then
For j = 1 To 17
compareRow(j) = arrData(y, j).Value
Next j
For v = 1 To 17
If Val(compareRow(v)) <> Val(newRow(v)) Then
match = False
Exit For
Else
match = True
End If
Next v
If match = True Then
Exit For
End If
Else
match = False
End If
Next y
y = 2
If match = False Then
rowCount = rowCount + 1
ReDim Preserve arrTemp(1 To 17, 1 To rowCount)
For t = 1 To 17
arrTemp(t, rowCount) = newRow(t)
Next t
End If
Next s
s = 18
Workbooks(nextFile).Close
nextFile = Dir()
Loop
'Transpose the array
ReDim arrOutput(1 To UBound(arrTemp, 2), 1 To UBound(arrTemp))
For C = LBound(arrTemp) To UBound(arrTemp)
For R = LBound(arrTemp, 2) To UBound(arrTemp, 2)
arrOutput(R, C) = arrTemp(C, R)
Next R
Next C
'Allocate back to the spreadsheet
With ThisWorkbook.Worksheets(1)
.Range(.Cells(2, 1), .Cells(UBound(arrOutput) + 1, 17)) = arrOutput
End With
secondsElapsed = Format((Timer - startTime) / 86400, "hh:mm:ss")
ThisWorkbook.Worksheets(2).Cells(1, 1) = secondsElapsed
End Sub
PS: As others suggested, is a good idea to use Option Explicit, and eventually to step through to code and see if everything is working as intended.
As for the Overflow issue... stepping through code would/should resolve that as well eventually. See Overflow (Error 6) for more info.
EDIT: I've added further management to holding the values in an array, and writing back to the spreadsheet.
Here's a revamp of your code that should be quicker and more memory friendly. (updated to be able to handle any number of results).
Sub getCBU()
Dim wb As Workbook
Dim wsDest As Worksheet
Dim wsTime As Worksheet
Dim hUnqVals As Object
Dim hUnqRows As Object
Dim aHeaders() As Variant
Dim aCompare() As Variant
Dim aResults() As Variant
Dim aStartingData() As Variant
Dim sFolder As String
Dim sFile As String
Dim sDelim As String
Dim sTemp As String
Dim lMaxResults As Long
Dim lCompareStartRow As Long
Dim lValCompareCol As Long
Dim ixCompare As Long
Dim ixResult As Long
Dim ixCol As Long
Dim dTimer As Double
dTimer = Timer
Set wb = ThisWorkbook
Set wsDest = wb.Worksheets(1)
Set wsTime = wb.Worksheets(2)
Set hUnqRows = CreateObject("Scripting.Dictionary")
Set hUnqVals = CreateObject("Scripting.Dictionary")
sDelim = "|"
lMaxResults = 100000
lCompareStartRow = 18
lValCompareCol = 11
sFolder = Environ("UserProfile") & "\Documents\CBU History\" 'Be sure to including ending \
sFile = Dir(sFolder & "CBU*.xlsx")
With wsDest.Range("A2:Q" & wsDest.Cells(wsDest.Rows.Count, lValCompareCol).End(xlUp).Row)
If .Row > 1 Then
aHeaders = .Offset(-1).Resize(1).Value
aStartingData = .Value
ReDim aResults(1 To lMaxResults, 1 To .Columns.Count)
For ixResult = 1 To UBound(aStartingData, 1)
For ixCol = 1 To UBound(aStartingData, 2)
sTemp = sTemp & sDelim & aStartingData(ixResult, ixCol)
Next ixCol
If Not hUnqRows.Exists(sTemp) Then hUnqRows.Add sTemp, sTemp
If Not hUnqVals.Exists(aStartingData(ixResult, lValCompareCol)) Then hUnqVals.Add aStartingData(ixResult, lValCompareCol), aStartingData(ixResult, lValCompareCol)
sTemp = vbNullString
Next ixResult
Erase aStartingData
Else
'No data to compare against, so no data can be added, exit macro
MsgBox "No data found in [" & wsDest.Name & "]" & Chr(10) & "Exiting Macro.", , "Error"
Exit Sub
End If
End With
With Application
.Calculation = xlCalculationManual
.EnableEvents = False
.ScreenUpdating = False
End With
ixResult = 0
Do While Len(sFile) > 0
Application.StatusBar = "Processing " & sFile & "..."
With Workbooks.Open(sFolder & sFile, , True).Worksheets(1)
With .Range("A" & lCompareStartRow & ":Q" & .Cells(.Rows.Count, lValCompareCol).End(xlUp).Row)
If .Row >= lCompareStartRow Then
aCompare = .Value
For ixCompare = 1 To UBound(aCompare, 1)
If hUnqVals.Exists(aCompare(ixCompare, lValCompareCol)) Then
For ixCol = 1 To UBound(aCompare, 2)
sTemp = sTemp & sDelim & aCompare(ixCompare, ixCol)
Next ixCol
If Not hUnqRows.Exists(sTemp) Then
hUnqRows.Add sTemp, sTemp
ixResult = ixResult + 1
For ixCol = 1 To UBound(aCompare, 2)
aResults(ixResult, ixCol) = aCompare(ixCompare, ixCol)
Next ixCol
If ixResult = lMaxResults Then OutputResults wsDest, aResults, ixResult, aHeaders
End If
sTemp = vbNullString
End If
Next ixCompare
Erase aCompare
End If
End With
.Parent.Close False
End With
sFile = Dir()
Loop
Application.StatusBar = vbNullString
If ixResult > 0 Then OutputResults wsDest, aResults, ixResult, aHeaders
wsTime.Range("A1").Value = Format((Timer - dTimer) / 86400, "hh:mm:ss")
With Application
.Calculation = xlCalculationAutomatic
.EnableEvents = True
.ScreenUpdating = True
End With
End Sub
Sub OutputResults(ByRef arg_ws As Worksheet, ByRef arg_aResults As Variant, ByRef arg_ixResult As Long, ByVal arg_aHeaders As Variant)
Static wsDest As Worksheet
Dim rDest As Range
Dim lMaxRows As Long
Dim lMaxCols As Long
If wsDest Is Nothing Then Set wsDest = arg_ws
lMaxRows = UBound(arg_aResults, 1)
lMaxCols = UBound(arg_aResults, 2)
Set rDest = wsDest.Range("A1").Resize(, lMaxCols).EntireColumn.Find("*", wsDest.Range("A1"), xlValues, xlWhole, , xlPrevious)
If rDest Is Nothing Then Set rDest = wsDest.Range("A2") Else Set rDest = wsDest.Cells(rDest.Row, "A")
If rDest.Row + 1 + arg_ixResult > wsDest.Rows.Count Then
Set wsDest = wsDest.Parent.Worksheets.Add(After:=wsDest)
With wsDest.Range("A1").Resize(, lMaxCols)
.Value = arg_aHeaders
.Font.Bold = True
End With
Set rDest = wsDest.Range("A2")
End If
rDest.Resize(arg_ixResult, lMaxCols).Value = arg_aResults
Erase arg_aResults
ReDim arg_aResults(1 To lMaxRows, 1 To lMaxCols)
End Sub
I have a userform of 7 checkboxes and some labels that describe them. For each corresponding checkbox there is an array from which there will be created a report if the checkbox is checked as true. However, it doesn't loop through correctly.
I want it to loop through as A, B, C, D, E, F, G for for each checkbox who have the TabIndex numbers of 0, 1, 2, 3, 4, 5, 6 respectively. However it loops through in the order of 0,6,5,4,3,2,1.
I have a main sub that defines and declares variables. My userform print code is as follows:
Sub Get_PDF_Click()
' Creating PDF
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
PDFUserForm.Hide
i = 0
j = 0
For Each ctl In Me.Controls
If TypeName(ctl) = "CheckBox" Then
If ctl.Value = True Then
j = j + 1
Name_of_File = Array(i + 1, 1) & " report" & YYMM & ".xlsx"
Workbooks.Open Filename:=OutputPath & Name_of_File
Set Wkb = Workbooks(Name_of_File)
For Each ws In Wkb.Worksheets
PDF_Name = Array(i + 1, 1) & " " & ws.Name & " " & YYMM
ws.ExportAsFixedFormat Type:=xlTypePDF, Filename:= _
OutputPath & PDF_Name, Quality _
:=xlQualityStandard, IncludeDocProperties:=True, IgnorePrintAreas _
:=False, OpenAfterPublish:=False
Next ws
Wkb.Close SaveChanges:=False
End If ' See if checked
i = i + 1
Debug.Print ctl.Name
End If ' See if checkbox
Next ctl
If j > 0 Then
' Notification on process time
SecondsElapsed = Round(Timer - StartTime, 0)
MsgBox "PDF succesfully published after " & SecondsElapsed & " seconds." & Chr(10) & "Location: " & OutputPath, vbInformation
Else
MsgBox "No file was selected.", vbInformation
End If
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
As an aside I have a similar problem in another piece of code where I loop through charts on a worksheet which is also looping in the wrong order, so perhaps the same solution concept can be applied to that.
For Each isn't specified to guarantee an enumeration order. In all likelihood the controls are being enumerated in the order they were added to the Me.Controls collection.
If you need a specific order, use a For loop:
Dim checkboxNames As Variant
checkboxNames = Array("chkA", "chkB", "chkC", "chkD", "chkE", ...)
Dim current As Long, checkboxName As String, currentBox As MSForms.CheckBox
For current = LBound(checkboxNames) To UBound(checkboxNames)
checkboxName = checkboxNames(current)
Set currentBox = Me.Controls(checkboxName)
'work with the currentBox here
Next
Note that this also removes the need to iterate controls you're not interested in
Here is another way ;) This does not need you to hard code the name of the checkboxes.
Logic: Create a 2D array. Store Tabindex and CheckBox Name in the array. Sort it on Tabindex and use it as you want it :)
Code:
Option Explicit
Private Sub Sample()
Dim CbArray() As String
Dim n As Long: n = 1
Dim cbCount As Long
Dim tindex As String, ctlname As String
Dim ctl As Control
Dim i As Long, j As Long
For Each ctl In Me.Controls
If TypeName(ctl) = "CheckBox" Then
n = n + 1
End If
Next
n = n - 1: cbCount = n
ReDim CbArray(1 To n, 1 To 2)
n = 1
'~~> Sort the Tabindex and checkbox name in the array
For Each ctl In Me.Controls
If TypeName(ctl) = "CheckBox" Then
CbArray(n, 1) = ctl.TabIndex
CbArray(n, 2) = ctl.Name
n = n + 1
End If
Next
'~~> Sort the array
For i = 1 To cbCount
For j = i + 1 To cbCount
If CbArray(i, 1) < CbArray(j, 1) Then
tindex = CbArray(j, 1)
ctlname = CbArray(j, 2)
CbArray(j, 1) = CbArray(i, 1)
CbArray(j, 2) = CbArray(i, 2)
CbArray(i, 1) = tindex
CbArray(i, 2) = ctlname
End If
Next j
Next i
'~~> Loop through the checkboxes
For i = cbCount To 1 Step -1
With Controls(CbArray(i, 2))
Debug.Print .Name
'
'~~> Do what you want
'
End With
Next i
End Sub
Hopefully the title is clear. I am trying to search through multiple tables on a single sheet. The information I am looking for is the same for all of the tables, just that the corresponding column is located in different spots (e.g. in one table the column I want to search is in I, while for another table it could be in O.) which makes it a bit more challenging for me.
I want to search through each column that has the same title (Load Number) and depending on its value, copy that entire row over to a sheet that corresponds with that value.
Below is what I have so far in VBA as well as a picture to hopefully clarify my issue.
Any help/advice is appreciated!
http://imgur.com/a/e9DyH
Sub Load_Number_Finder()
Dim ws As Worksheet
Dim i As Integer
Dim j As Integer
j = 1
Set ws = Sheets.Add(After:=Sheets("Master"))
ws.Name = ("Test Load " & j)
i = 1
Sheets("Master").Select
For Each cell In Sheets("Master").Range("M:M")
If cell.Value = "1" Then
j = 1
'Set WS = Sheets.Add(After:=Sheets("Master"))
'WS.Name = ("Test Load " & j)
matchRow = cell.Row
Rows(matchRow & ":" & matchRow).Select
Selection.Copy
Sheets("Test Load " & j).Select
ActiveSheet.Rows(i).Select
ActiveSheet.Paste
Sheets("Master").Select
i = i + 1
ElseIf cell.Value = "" Then
' 2, 3, 4, 5, cases
Else
' Something needs to go here to catch when it doesnt have a load number on it yet
End If
' Err_Execute:
' MsgBox "An error occurred."
Next
End Sub
Try this function. This should work for you. Let me know what the results are with your sheet. I made a mock up sheet and tested it, it worked. I can make changes if this is not exactly what you are looking for.
Option Explicit
Sub copyPaste()
Dim rowCount, row_ix, temp, i As Integer
Dim TD_COL_IX As Integer
Dim td_value As String
Dim td_values() As String
rowCount = Worksheets("Master").Cells(Rows.Count, "A").End(xlUp).Row
For row_ix = 1 To rowCount
temp = isNewTable(CInt(row_ix))
If temp > 0 Then
TD_COL_IX = temp
ElseIf TD_COL_IX > 0 Then
td_value = Worksheets("Master").Cells(row_ix, TD_COL_IX)
If Not td_value = "" Then
td_values = Split(td_value, " ")
For i = 0 To UBound(td_values)
If Not sheetExists("Test Load " & td_values(i)) Then
Sheets.Add.Name = "Test Load " & td_values(i)
End If
If Worksheets("Test Load " & td_values(i)).Cells(1, 1).Value = "" Then
Worksheets("Master").Range(Worksheets("Master").Cells(row_ix, 1), Worksheets("Master").Cells(row_ix, TD_COL_IX - 1)).Copy _
Destination:=Worksheets("Test Load " & td_values(i)).Cells(1, 1)
Else
Dim rowCount_pasteSheet As Integer
rowCount_pasteSheet = Worksheets("Test Load " & td_values(i)).Cells(Rows.Count, "A").End(xlUp).Row
Worksheets("Master").Range(Worksheets("Master").Cells(row_ix, 1), Worksheets("Master").Cells(row_ix, TD_COL_IX - 1)).Copy _
Destination:=Worksheets("Test Load " & td_values(i)).Cells(rowCount_pasteSheet + 1, 1)
End If
Next i
End If
End If
Next row_ix
End Sub
Function isNewTable(row_ix As Integer) As Integer
Dim colCount, col_ix As Integer
colCount = Worksheets("Master").Cells(row_ix, Columns.Count).End(xlToLeft).Column
For col_ix = 1 To colCount
If Not IsError(Worksheets("Master").Cells(row_ix, col_ix).Value) Then
If Worksheets("Master").Cells(row_ix, col_ix).Value = "LD #" Then
isNewTable = col_ix
Exit Function
End If
End If
Next col_ix
isNewTable = 0
End Function
' ####################################################
' sheetExists(sheetToFind As String) As Boolean
'
' Returns true if the sheet exists, False otherwise
' ####################################################
Public Function sheetExists(sheetToFind As String) As Boolean
Dim sheet As Worksheet
sheetExists = False
For Each sheet In Worksheets
If sheetToFind = sheet.Name Then
sheetExists = True
Exit Function
End If
Next sheet
End Function