Getting My Script To Run Across All Worksheets (Excel) - vba

This code works on one sheet and now am trying get it to work across multiple sheets, avoiding the first two sheets ("AA" and "Word Frequency").
Original code here (See #Jeeped's answer)
Link to Worksheet here
Was trying to adapt code from related threads I found (Reference 1, 2) however I do not know how (and whether) to apply the Ws.Name and Ws.Range objects into my existing code.
It seems like the code activates Sheet1 using With Worksheets("Sheet1") and I was trying to replace this with the following method:
Create For looped function byGroupCounter() to identify how many worksheets there are, and run across all existing worksheets. Each worksheet will be incremented with variable "i"
For loop in byGroupCounter() calls on function byGroup(i) to run the original code on the selected worksheet (ie. worksheet "i")
The byGroup() function runs it's process across worksheet i.
Part where I believe I'm getting an error: Replacing the With Worksheets("Sheet1") code to With Ws, where Ws = Worksheets(Sheet_Index) and Sheet_Index is equal to i, defined from byGroupCounter()
I believe I have to add the Ws prefix in front of .Range but everything I've been trying, I keep getting the error "Can't execute code in break mode".
Current Code:
Sub byGroupCounter()
Dim i As Integer
Application.ScreenUpdating = False
For i = ActiveSheet.Index To Sheets.Count
byGroup i
Next i
Application.ScreenUpdating = True
End Sub
Sub byGroup(ByVal Sheets_Index As Integer)
Dim g As Long, s As Long, aSTRs As Variant, aGRPs As Variant
Dim Ws As Worksheet
Set Ws = Worksheets(Sheet_Index)
appTGGL bTGGL:=False
' I believe the next line is where I am doing something wrong:
With Ws
aSTRs = .Range(.Cells(2, 1), .Cells(Rows.Count, 1).End(xlUp)).Value2
With .Range(.Cells(1, 5), .Cells(Rows.Count, 1).End(xlUp).Offset(0, Application.Match("zzz", .Rows(1)) - 1))
.Resize(.Rows.Count, .Columns.Count).Offset(1, 0).ClearContents
aGRPs = Ws.Cells.Value2
End With
For s = LBound(aSTRs, 1) To UBound(aSTRs, 1)
For g = LBound(aGRPs, 2) To UBound(aGRPs, 2)
If CBool(InStr(1, aSTRs(s, 1), aGRPs(1, g), vbTextCompare)) Then
aGRPs(s + 1, g) = aSTRs(s, 1)
Exit For
End If
Next g
Next s
.Cells(1, 5).Resize(UBound(aGRPs, 1), UBound(aGRPs, 2)) = aGRPs
End With
appTGGL
End Sub
Public Sub appTGGL(Optional bTGGL As Boolean = True)
Debug.Print Timer
Application.ScreenUpdating = bTGGL
Application.EnableEvents = bTGGL
Application.DisplayAlerts = bTGGL
Application.Calculation = IIf(bTGGL, xlCalculationAutomatic, xlCalculationManual)
End Sub

There are just 6 changes to the original code to loop through the sheets
I have them commented with '<<<
Sub byGroup()
Dim g As Long, s As Long, aSTRs As Variant, aGRPs As Variant, sh As Worksheet '<<<
appTGGL bTGGL:=False
For Each sh In Sheets '<<<
If sh.Name <> "AA" And sh.Name <> "Word Frequency" Then '<<<<
With sh '<<<
aSTRs = .Range(.Cells(2, 1), .Cells(Rows.Count, 1).End(xlUp)).Value2
With .Range(.Cells(1, 5), .Cells(Rows.Count, 1).End(xlUp).Offset(0, Application.Match("zzz", .Rows(1)) - 1))
.Resize(.Rows.Count, .Columns.Count).Offset(1, 0).ClearContents
aGRPs = .Cells.Value2
End With
For s = LBound(aSTRs, 1) To UBound(aSTRs, 1)
For g = LBound(aGRPs, 2) To UBound(aGRPs, 2)
If CBool(InStr(1, aSTRs(s, 1), aGRPs(1, g), vbTextCompare)) Then
aGRPs(s + 1, g) = aSTRs(s, 1)
Exit For
End If
Next g
Next s
.Cells(1, 5).Resize(UBound(aGRPs, 1), UBound(aGRPs, 2)) = aGRPs
End With
End If '<<<<
Next sh '<<<
appTGGL
End Sub
Public Sub appTGGL(Optional bTGGL As Boolean = True)
Debug.Print Timer
Application.ScreenUpdating = bTGGL
Application.EnableEvents = bTGGL
Application.DisplayAlerts = bTGGL
Application.Calculation = IIf(bTGGL, xlCalculationAutomatic, xlCalculationManual)
End Sub

Related

VBA Excel Script - Runtime Error 424 (Code Specific)

By no means am I a VBA developer, but any help on why this isn't working would be greatly appreciated...
Problem:
Analyze all worksheets, except the last.
Check if a column I and J contain an X, if they do, get that row and copy it to the last worksheet.
Error Highlighted is at this line: For Each ws In Workbook.Worksheets. I'm not sure why.
Below is my code, but it's not compiling, and giving me the error code 424 - Object Required.
Sub CopyData()
Application.ScreenUpdating = False
Dim pasteSheet As Worksheet
Set pasteSheet = Worksheets("Remediation Summary")
For Each ws In ThisWorkbook.Worksheets
If ws.Index < (ThisWorkbook.Worksheets.Count - 1) Then
'check column i for x
For Each icell In ws.Range("i1:i200").Cells
If icell.Value Like ("X") Or ("x") Then
Rows(icell.RowIndex).Copy
pasteSheet.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
Application.CutCopyMode = False
Application.ScreenUpdating = True
End If
Next icell
'check column j for x
For Each jcell In ws.Range("j1:j200").Cells
If jcell.Value Like ("X") Or ("x") Then
Rows(jcell.RowIndex).Copy
pasteSheet.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
Application.CutCopyMode = False
Application.ScreenUpdating = True
End If
Next jcell
End If
Next ws
End Sub
Option Explicit is really a great helper - write it on the top of every module / class / worksheet. It would tell immediately, if there is some variable, which is not declared.
In your case, ws should be declared as a worksheet, as far as you are using the for-each loop to go through the Worksheets collection:
Option Explicit
Sub CopyData()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
Debug.Print ws.Name
Next ws
End Sub
Option Explicit MSDN
Concerning this part - If icell.Value Like ("X") Or ("x") Then, consider rewriting it like this:
If UCase(icell) = "X" Then. It would be more understandable and Like is not needed when the comparison is without some additional signs ?*.
Excel VBA like operator
updated codebase:
Sub CopyData()
Application.ScreenUpdating = False
Dim pasteSheet As Worksheet
Set pasteSheet = Worksheets("Remediation Summary")
For Each ws In ThisWorkbook.Worksheets
If ws.Index < (ThisWorkbook.Worksheets.Count - 1) Then
'check column i for x
For Each icell In ws.Range("i0:i200").Cells
If icell.Value Like ("X") Or ("x") Then
Rows(icell.RowIndex).Copy
pasteSheet.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
Application.CutCopyMode = False
Application.ScreenUpdating = True
End If
Next icell
'check column j for x
For Each jcell In ws.Range("j0:j200").Cells
If jcell.Value Like ("X") Or ("x") Then
Rows(jcell.RowIndex).Copy
pasteSheet.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
Application.CutCopyMode = False
Application.ScreenUpdating = True
End If
Next jcell
End If
Next ws
End Sub
Based on my test, please try the code below:
Option Explicit
Sub CopyData()
Application.ScreenUpdating = False
Dim pasteSheet As Worksheet
Dim ws As Worksheet
Dim icell As Range
Dim jcell As Range
Set pasteSheet = Worksheets("Remediation Summary")
For Each ws In ThisWorkbook.Worksheets
If ws.Index < (ThisWorkbook.Worksheets.Count - 1) Then
'check column i for x
For Each icell In ws.Range("i1:i200").Cells
If UCase(icell) = "X" Or UCase(icell) = "x" Then
pasteSheet.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).EntireRow.Value = icell.EntireRow.Value
Application.CutCopyMode = False
Application.ScreenUpdating = True
End If
Next icell
'check column j for x
For Each jcell In ws.Range("j1:j200").Cells
If UCase(jcell) = "X" Or UCase(jcell) = "x" Then
pasteSheet.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).EntireRow.Value = jcell.EntireRow.Value
Application.CutCopyMode = False
Application.ScreenUpdating = True
End If
Next jcell
End If
Next ws
End Sub
Sub CopyData()
Dim pasteSheet As Worksheet, ws As Worksheet, icell As Range
Set pasteSheet = Worksheets("Remediation Summary") 'ThisWorkbook?
For Each ws In ThisWorkbook.Worksheets
If ws.Index < (ThisWorkbook.Worksheets.Count - 1) Then
'check column i,j for x
For Each icell In ws.Range("i1:i200").Cells
If LCase(icell.Value) = "x" Or LCase(icell.Offset(0, 1).Value) = "x" Then
pasteSheet.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).EntireRow.Value = _
icell.EntireRow.Value
End If
Next icell
End If
Next ws
End Sub

VBA Copy column name from one sheet to all other sheets

I'm stuck on a code with
Runtime Error 424, Object required
The code is basically copying a column from the first sheet name "Generate" and transpose the copied column to a header row on all other active sheets except "Generate".
Could anyone help me to fix the error?
Sub Test()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Worksheets
If ws.Name <> "Generate" Then
Worksheets("Generate").Range("B2:B42").Copy
ActiveWorksheet.Range("A" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial Transpose:=True
End If
Next ws
End Sub
Try collecting the column header labels into an array first.
sub test()
dim hdrs as variant, w as long
with worksheets(1)
hdrs = application.transpose(.range(.cells(2, "B"), .cells(.rows.count, "B").end(xlup)).value2)
end with
for w=2 to worksheets.count
with worksheets(w)
.cells(1, "A").resize(1, ubound(hdrs)) = hdrs
'.cells(.rows.count, "A").end(xlup).offset(1, 0).resize(1, ubound(hdrs)) = hdrs
end with
next w
end sub
'alternate by worksheet name
sub test()
dim hdrs as variant, w as long
with worksheets("Generate")
hdrs = application.transpose(.range(.cells(2, "B"), .cells(.rows.count, "B").end(xlup)).value2)
end with
for w=1 to worksheets.count
if lcase(worksheets(w).name) <> "generate" then
with worksheets(w)
.cells(1, "A").resize(1, ubound(hdrs)) = hdrs
'.cells(.rows.count, "A").end(xlup).offset(1, 0).resize(1, ubound(hdrs)) = hdrs
end with
end if
next w
end sub

Faster code to delete cells through multiple worksheets in excel

i am a beginner in VB and having googled and looked through the answers here i have written the following loop to cycle through multiple excel worksheets and delete rows where the cells contain specific elements (N/A # N/A#).
The data in the xl sheet to be cleaned is financial data with DATE, OPEN. HIGH LOW CLOSE. the number of rows can be significant and the number of worksheets can be 2-300. It works but is very very slow and as I am learning - would appreciate any assistance on how i can make this code faster. Thank you.
Sub DataDeleteStage1()
ScreenUpdating = False
Dim lrow As Long
Dim ws As Worksheet
Dim icntr As Long
For Each ws In ThisWorkbook.Worksheets
lrow = ws.Cells(Rows.CountLarge, "a").End(xlUp).Row
For icntr = lrow To 1 Step -1
If ws.Name <> "HEADER" Then
If ws.Cells(icntr, "B") = "#N/A N/A" And ws.Cells(icntr, "C") = "#N/A N/A" And ws.Cells(icntr, "D") = "#N/A N/A" And ws.Cells(icntr, "E") = "#N/A N/A" Then
ws.Rows(icntr).EntireRow.Delete
End If
End If
Next icntr
Next ws
End Sub
Try merging all Ranges to be deleted to a MergeRng object, and then just delete it all at once.
Code
Sub DataDeleteStage1()
ScreenUpdating = False
Dim lrow As Long
Dim ws As Worksheet
Dim icntr As Long
Dim MergeRng As Range
For Each ws In ThisWorkbook.Worksheets
With ws
lrow = .Cells(.Rows.Count, "A").End(xlUp).Row
For icntr = lrow To 1 Step -1
If .Name <> "HEADER" Then
If .Cells(icntr, "B") = "#N/A N/A" And .Cells(icntr, "C") = "#N/A N/A" And .Cells(icntr, "D") = "#N/A N/A" And .Cells(icntr, "E") = "#N/A N/A" Then
If Not MergeRng Is Nothing Then
Set MergeRng = Application.Union(MergeRng, .Rows(icntr))
Else
Set MergeRng = .Rows(icntr)
End If
End If
End If
Next icntr
' Delete all rows at once
If Not MergeRng Is Nothing Then MergeRng.Delete
End With
Set MergeRng = Nothing ' reset range when changing worksheets
Next ws
End Sub
You can make your code delete only once and not every time.
In order to make it like this, try the following:
Sub DataDeleteStage1()
Application.ScreenUpdating = False
Dim lrow As Long
Dim ws As Worksheet
Dim icntr As Long
Dim delRange As Range
For Each ws In ThisWorkbook.Worksheets
lrow = ws.Cells(Rows.CountLarge, "a").End(xlUp).Row
For icntr = lrow To 1 Step -1
If ws.Name <> "HEADER" Then
If ws.Cells(icntr, "B") = "#N/A N/A" And _
ws.Cells(icntr, "C") = "#N/A N/A" And _
ws.Cells(icntr, "D") = "#N/A N/A" And _
ws.Cells(icntr, "E") = "#N/A N/A" Then
If Not delRange Is Nothing Then
Set delRange = ws.Rows(icntr)
Else
Set delRange = Union(delRange, ws.Rows(icntr))
End If
End If
End If
Next icntr
If Not delRange Is Nothing Then delRange.Delete
Set delRange = Nothing
Next ws
End Sub
I have not tried it, but it should work.
How about this?
Sub DeleteRows()
Dim ws As Worksheet
With Application
.Calculation = xlCalculationManual
.EnableEvents = False
.ScreenUpdating = False
End With
For Each ws In ThisWorkbook.Sheets
If ws.Name <> "HEADER" Then
On Error Resume Next
ws.Columns("B:E").Replace "#N/A N/A", "=NA()"
ws.Columns("B:E").SpecialCells(xlCellTypeFormulas, 16).EntireRow.Delete
End If
Next ws
With Application
.Calculation = xlCalculationAutomatic
.EnableEvents = True
.ScreenUpdating = True
End With
End Sub
With AutoFilter and without looping altogether:
Sub DataDeleteStage1()
Dim ws As Worksheet
Dim lr As Integer
Application.ScreenUpdating = False
For Each ws In ThisWorkbook.Worksheets
With ws
lr = .Range("A" & .Rows.Count).End(xlUp).Row
If ws.Name <> "HEADER" Then
.UsedRange.AutoFilter Field:=2, Criteria1:="#N/A"
.UsedRange.AutoFilter Field:=3, Criteria1:="#N/A"
.UsedRange.AutoFilter Field:=4, Criteria1:="#N/A"
.UsedRange.AutoFilter Field:=5, Criteria1:="#N/A"
.Range("A2:A" & lr).SpecialCells(xlCellTypeVisible).EntireRow.Delete shift:=xlUp
End If
End With
Next ws
Application.ScreenUpdating = True
End Sub
Tested this vs. the merged range approach on 300K rows - faster by minutes when doing multiple sheets.
I haven't tested but try this,
Sub DataDeleteStage1()
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Dim lrow As Long
Dim ws As Worksheet
Dim icntr As Long
For Each ws In ThisWorkbook.Worksheets
lrow = ws.Cells(Rows.CountLarge, "a").End(xlUp).Row
If ws.Name <> "HEADER" Then
On Error Resume Next
Range("F1:F" & lrow).Formula = "=IF(SUMPRODUCT(--ISERROR(A1:E1))=5,NA(),"""")"
Range("F1:F" & lrow).SpecialCells(xlCellTypeFormulas, xlErrors).EntireRow.Delete shift:=xlUp
Range("F1:F" & lrow).Clear
End If
Next ws
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub

Delete all rows if duplicate in excel - VBA

I need to remove all rows without leaving any unique record. If duplicate exists delete all matching rows. Criteria is column C if any duplicate record exists in column C then delete entire row (including unique).
Below given code is working but leaving the unique row Even I don't want that.
Code:
Sub DDup()
Sheets("MobileRecords").Activate
With ActiveSheet
Set Rng = Range("A1", Range("C1").End(xlDown))
Rng.RemoveDuplicates Columns:=Array(3, 3), Header:=xlYes
End With
End Sub
I like the code from Jeeped, but it isn't the best readable one. Therefore, here is another solution.
Sub remDup()
Dim rng As Range, dupRng As Range, lastrow As Long, ws As Worksheet
Dim col As Long, offset As Long, found As Boolean
'Disable all the stuff that is slowing down
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
'Define your worksheet here
Set ws = Worksheets(1)
'Define your column and row offset here
col = 3
offset = 0
'Find first empty row
Set rng = ws.Cells(offset + 1, col)
lastrow = rng.EntireColumn.Find( _
What:="", After:=ws.Cells(offset + 1, col)).Row - 1
'Loop through list
While (rng.Row < lastrow)
Do
Set dupRng = ws.Range(ws.Cells(rng.Row + 1, col), ws.Cells(lastrow, col)).Find( _
What:=rng, LookAt:=xlWhole)
If (Not (dupRng Is Nothing)) Then
dupRng.EntireRow.Delete
lastrow = lastrow - 1
found = True
If (lastrow = rng.Row) Then Exit Do
Else
Exit Do
End If
Loop
Set rng = rng.offset(1, 0)
'Delete current row
If (found) Then
rng.offset(-1, 0).EntireRow.Delete
lastrow = lastrow - 1
End If
found = False
Wend
'Enable stuff again
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
End Sub
It works with more than one duplicate and you can define an row offset, which defines how much rows you ignore at the beginning of the column.
I like to try these without any declared variables. It is good practise for keeping your cell / worksheet / workbook hierarchy together.
Sub dupeNuke()
With Worksheets("Sheet1") '<~~ you should know what worksheet you are supposed to be on
If .AutoFilterMode Then .AutoFilterMode = False
With .Cells(1, 1).CurrentRegion
With .Resize(.Rows.Count - 1, 1).Offset(1, 2)
With .FormatConditions
.Delete
.Add Type:=xlExpression, Formula1:="=COUNTIF(C:C, C2)>1"
End With
With .FormatConditions(.FormatConditions.Count)
.Interior.Color = vbRed
End With
End With
With .Resize(.Rows.Count, 1).Offset(0, 2)
.AutoFilter Field:=1, Criteria1:=vbRed, Operator:=xlFilterCellColor
With .Resize(.Rows.Count - 1, .Columns.Count).Offset(1, 0)
If CBool(Application.Subtotal(103, Cells)) Then
.EntireRow.Delete
End If
End With
End With
With .Resize(.Rows.Count - 1, 1).Offset(1, 2)
With .FormatConditions
.Delete
End With
End With
End With
If .AutoFilterMode Then .AutoFilterMode = False
End With
End Sub
Obviously, this is heavily reliant on the With ... End With statement. An underrated / underused method in my estimation.

Excel Add Sheet or Overwrite if Exists

In excel I have a macro that converts all of columns of an active sheet into an new sheet called "MasterList"
My problem is when I rerun that macro I get an error saying "That name is already taken." Try a different one.
I need my macro to overwrite MaterList sheet if it already exists.
Here is my code:
Sub ToArrayAndBack()
Dim arr As Variant, lLoop1 As Long, lLoop2 As Long
Dim arr2 As Variant, lIndex As Long
'turn off updates to speed up code execution
With Application
.ScreenUpdating = False
.EnableEvents = False
.Calculation = xlCalculationManual
.DisplayAlerts = False
End With
ReDim arr2(ActiveSheet.UsedRange.Cells.Count - ActiveSheet.UsedRange.SpecialCells(xlCellTypeBlanks).Count)
arr = ActiveSheet.UsedRange.Value
For lLoop1 = LBound(arr, 1) To UBound(arr, 1)
For lLoop2 = LBound(arr, 2) To UBound(arr, 2)
If Len(Trim(arr(lLoop1, lLoop2))) > 0 Then
arr2(lIndex) = arr(lLoop1, lLoop2)
lIndex = lIndex + 1
End If
Next
Next
Sheets.Add.Name = "MasterList"
Range("A1").Resize(, lIndex + 1).Value = arr2
Range("A1").Resize(, lIndex + 1).Copy
Range("A2").Resize(lIndex + 1).PasteSpecial Transpose:=True
Rows(1).Delete
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = xlCalculationAutomatic
.DisplayAlerts = True
End With
End Sub
You can put the sheet creation between on error resume and on error goto 0. The other solution is to loop through the workbook sheets collection and check if a sheet with that name exists.
Solution 1:
On Error Resume Next
Sheets.Add.Name = "MasterList"
On Error GoTo 0
Solution 2:
Dim ws As Worksheet
Dim found As Boolean
found = False
For Each ws In ThisWorkbook.Sheets
If ws.Name = "MasterList" Then
found = True
Exit For
EndIf
Next
If Not found Then
Sheets.Add.Name = "MasterList"
EndIf
To avoid relying on the fact that MasterList is active:
Set ws = ThisWorkbook.Sheets("MasterList")
With ws
.Range("A1").Resize(, lIndex + 1).Value = arr2
.Range("A1").Resize(, lIndex + 1).Copy
.Range("A2").Resize(lIndex + 1).PasteSpecial Transpose:=True
.Rows(1).Delete
End With