Delete Entire Column if column only consists of zeros and blank values - excel

I was wondering if someone could help me with this excel VBA challenge i faced,
I am trying to delete a column if a column has only 0 and blank values, the format looks like the below picture:
As i have highlighted the two columns has only zero and blank values and those 2 columns should be deleted.
Excel Column Format
I have tried this code but unfortunately it deletes all the columns:
Sub dynamicRange()
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
Application.ScreenUpdating = False
Dim startCell As Range, lastRow As Long, lastCol As Long, ws As Worksheet
Set ws = ActiveSheet
Set startCell = Range("E9")
lastRow = ws.Cells(ws.Rows.Count, startCell.Column).End(xlUp).Row
lastCol = ws.Cells(startCell.Row, ws.Columns.Count).End(xlToLeft).Column
ws.Range(startCell, ws.Cells(lastRow, lastCol)).Select
Set a = Selection
For Each cell In a
If cell.Value = "Total" Or cell.Value = "Tag" Or cell.Value = "Delivery Fee" Or cell.Value = "CC/Cash" Or cell.Value = "Postcode" Then
cell.EntireColumn.Delete
End If
Next cell
For Each cell In a
If cell.Value = 0 Or cell.Value = "" Then
cell.EntireColumn.Delete
End If
Next cell
Application.Calculation = xlCalculationManual
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
Looking forward for a solution, Thank you.

you can use count number of populated cells in range
WorksheetFunction.CountA(range)
This is sample code
sub test()
dim lasCol as integer
lastCol = Cells(1, Columns.Count).End(xlToLeft).Column
for i = lastCol to 1 Step - 1
if worksheetfunction.countA(Columns(i)) = 0 then
Columns(i).delete
end if
next i
end sub
Of course you can change Column to Range to check it content data or not that fit your file. eg
if worksheetfunction.countA(range("A2:A10, A15:A20")) = 0 then
Or
if worksheetfunction.countA(range(cells(2,i), cells(10,i))) + worksheetfunction.countA(range(cells(15,i), cells(20,i)))= 0 then

If you want to delete columns without value you can use Excel's own SUM() function with simple code as shown below.
Sub DynamicRange()
Dim startCell As Range
Dim SumRng As Range
Dim lastRow As Long, lastCol As Long
Dim C As Long
With Application
.EnableEvents = False
.Calculation = xlCalculationManual
.ScreenUpdating = False
End With
With ActiveSheet
Set startCell = .Range("E9")
lastRow = .Cells(.Rows.Count, startCell.Column).End(xlUp).Row
lastCol = .Cells(startCell.Row, .Columns.Count).End(xlToLeft).Column
For C = lastCol To startCell.Column Step -1
Set SumRng = .Range(.Cells(startCell.Row, C), .Cells(lastRow, C))
If Application.Sum(SumRng) = 0 Then .Columns(C).Delete
Next C
End With
With Application
.EnableEvents = True
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
I'm not sure I understand the significance of your startCell at E9 correctly. Your code doesn't appear to match with the picture of your worksheet in that respect. My code ignores values which are above row 9 but that would be very easy to modify. Just let me know. The point is that using the SUM() function makes the code run much faster than having to examine every cell.

Delete Zero-Blank Columns
Option Explicit
Sub DeleteEmptyColumns()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
Dim rng As Range, URng As Range, startCell As Range
Dim lastRow As Long, lastCol As Long, ws As Worksheet
Dim j As Long ' Column Counter
Dim i As Long ' Row Counter
On Error GoTo ProgramError
Set ws = ActiveSheet
Set startCell = ws.Range("E9")
lastRow = ws.Cells(ws.Rows.Count, startCell.Column).End(xlUp).Row
lastCol = ws.Cells(startCell.Row, ws.Columns.Count).End(xlToLeft).Column
For j = startCell.Column To lastCol
For i = startCell.Row To lastRow
Set rng = ws.Cells(i, j)
If rng.Value <> 0 And rng.Value <> "" Then Exit For
Next
If i > lastRow Then Set rng = ws.Cells(1, j): GoSub UnionRange
Next
' ' While developing such a code, use Hidden instead of Delete.
' If Not URng Is Nothing Then URng.EntireColumn.Hidden = True
If Not URng Is Nothing Then URng.EntireColumn.Delete
MsgBox "Operation finished successfully."
SafeExit:
Application.EnableEvents = True
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = True
Exit Sub
UnionRange:
If Not URng Is Nothing Then
Set URng = Union(URng, rng)
Else
Set URng = rng
End If
Return
ProgramError:
MsgBox "An unexpected error occurred."
On Error GoTo 0
GoTo SafeExit
End Sub

Related

Copy last 3 rows, excluding the rows for which there is a "0" in column "C"

I have a problem.
I want to find the last row in another file and sheet and copy the last 3 rows from A-AD, except those that have a "0" in column "C". I want the number of copied rows to always be 3.
I have a problem with the code below because it always only copies just one row in the end.
Sub AB ()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim ws As Worksheet
Dim lastRow As Long, i As Long
Dim numCopied As Long
Dim baseWB As Workbook, baseWS As Worksheet
Dim spWB As Workbook, spWS As Worksheet
Set baseWB = ThisWorkbook
Set baseWS = ActiveSheet
lastRow = spWS.Cells(spWS.Rows.Count, "D").End(xlUp).Row
numCopied = 0
For i = lastRow To lastRow - 8 Step -1
' Sprawdź, czy w kolumnie C jest 0
If spWS.Cells(i, "C").Value <> 0 Then
spWS.Range(spWS.Cells(i, "A"), spWS.Cells(i, "AD")).Copy
numCopied = numCopied + 1
End If
If numCopied = 3 Then
Exit For
End If
Next i
baseWB.Sheets("Sheet1").Range("E5").PasteSpecial xlPasteValues
spWB.Close SaveChanges:=False
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
The copy inside the loop is overwriting the previous copy. They are not additive unless you use Union.
Option Explicit
Sub AB()
Dim spWB As Workbook, spWS As Worksheet
Dim baseWB As Workbook, baseWS As Worksheet
Dim rng As Range, rngCopy As Range
Dim lastRow As Long, i As Long, numCopied As Long
Set baseWB = ThisWorkbook
Set baseWS = baseWB.Sheets("Sheet1")
' open workbook to copy from
Set spWB = Workbooks.Open("Source.xlsx", ReadOnly:=True)
Set spWS = spWB.Sheets("Sheet1")
numCopied = 0
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
With spWS
lastRow = .Cells(.Rows.Count, "D").End(xlUp).Row
For i = lastRow To 1 Step -1
' Sprawdz, czy w kolumnie C jest 0
If .Cells(i, "C").Value <> 0 Then
Set rng = .Cells(i, "A").Resize(, 30) ' A:AD
If rngCopy Is Nothing Then
Set rngCopy = rng
Else
Set rngCopy = Union(rng, rngCopy)
End If
numCopied = numCopied + 1
End If
If numCopied = 3 Then
Exit For
End If
Next i
End With
' copy
If rngCopy Is Nothing Then
MsgBox "No rows found to copy", vbExclamation
Else
rngCopy.Copy
baseWS.Range("E5").PasteSpecial xlPasteValues
Application.CutCopyMode = False
MsgBox " Copied : " & rngCopy.Address, vbInformation
End If
spWB.Close SaveChanges:=False
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub

Adding a header for each group of data instead of a common header

The below code thanks to #FaneDuru helped me copying filtered data to a new sheet, what I need to tweak is copying each set of data with a separate header instead of one main header for all data and also cut data instead of copy
Code:
Sub CopyFilteredCustomersByCompanyNames()
Dim wb As Workbook, ws As Worksheet, lastR As Long, wsComp As Worksheet, dictC As Object
Dim rngFilt As Range, arrCust() As Variant, arrFilt, i As Long
arrCust = Array("108169651", "108169651", "108169430", "108169430", "108168704", "108169596") 'place here the 10 specific customers name
Set wb = ActiveWorkbook 'use here the workbook you need
Set ws = ActiveSheet 'use here the necessary sheet (the one to be processed)
If ws.FilterMode Then ws.ShowAllData
Set rngFilt = ws.Range("A1").CurrentRegion: ' Debug.Print rngFilt.Address: Stop
arrFilt = rngFilt.Value2 'place the range in an array for faster iteration
'extract the uneque Company Names:
Set dictC = CreateObject("Scripting.Dictionary")
For i = 2 To UBound(arrFilt)
If arrFilt(i, 1) <> "" Then
dictC(arrFilt(i, 1)) = dictC(arrFilt(i, 1)) + 1
End If
Next i
Application.ScreenUpdating = False 'optimization to make code faster
Dim keyC As Variant, rngF As Range
For Each keyC In dictC.Keys 'iterate between dictionary keys (A:A company names)
rngFilt.AutoFilter 1, keyC 'first filter by dict key
rngFilt.AutoFilter 4, arrCust, xlFilterValues 'second by array of customers numbers
Set wsComp = Nothing
'insert the necessary sheets, name them (if not existing), clear if existing and copy the filtered range
Application.EnableEvents = False: Application.Calculation = xlCalculationManual
Application.AutomationSecurity = msoAutomationSecurityForceDisable
On Error Resume Next
Set wsComp = wb.Worksheets(keyC)
On Error GoTo 0
If Not wsComp Is Nothing Then
wsComp.Cells.ClearContents
Else
Set wsComp = wb.Worksheets.Add(After:=ws)
wsComp.Name = keyC
End If
rngFilt.Rows(1).Copy ' copy the headers columns width
wsComp.Range("A1").Resize(, rngFilt.Rows(1).Columns.Count).PasteSpecial xlPasteColumnWidths
On Error Resume Next
Set rngF = rngFilt.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If Not rngF Is Nothing Then
rngF.Copy wsComp.Range("A1")
End If
ws.ShowAllData
Application.AutomationSecurity = msoAutomationSecurityByUI
Application.EnableEvents = True: Application.Calculation = xlCalculationAutomatic
Next keyC
Application.ScreenUpdating = True
MsgBox "Ready..."
End Sub
What I want data to look like (separate data by header)
[![enter image description here][1]][1]
Link to Faneduru profile: https://stackoverflow.com/users/2233308/faneduru
If I can't cut and paste data, I used the below code to filter for the copied data to new sheets and delete it by using the "Or formula" and filtered for True and deleted those rows as the filter only takes 2 criteria.
Sub Delete()
Dim ws As Worksheet
Dim rng As Range
Dim lastRow As Long
Set ws = ActiveWorkbook.Sheets("Sheet1")
lastRow = ActiveSheet.Range("B1").Rows.End(xlDown).Row
Set rng = ws.Range("A1:AD" & lastRow)
ActiveSheet.Range("AD1").Value = "TRUE/FALSE"
ActiveSheet.Range("AD2").Formula = "=OR(D2=""108169651"",D2=""108169430"",D2=""108168704"",D2=""108169596"")"
ActiveSheet.Range("AD2:AD" & lastRow).Formula = ActiveSheet.Range("AD2").Formula
With rng
.AutoFilter Field:=30, Criteria1:="True"
.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
' turn off the filters
ws.AutoFilterMode = False
End Sub
I added the call sub for the above code at the end of your (CopyFilteredCustomersByCompanyNames) Sub:
Option Explicit
Sub CopyFilteredCustomersByCompanyNames()
Dim wb As Workbook, ws As Worksheet, lastR As Long, wsComp As Worksheet, dictC As Object
Dim rngFilt As Range, arrCust() As Variant, arrFilt, i As Long
'Array of Distributors which we need to add in new sheets
arrCust = Array("108169651", "108169651", "108169430", "108169430", "108168704", "108169596") 'place here the 10 specific customers name
Set wb = ActiveWorkbook 'use here the workbook you need
Set ws = ActiveSheet 'use here the necessary sheet (the one to be processed)
'Clear all Filters if any
If ws.FilterMode Then ws.ShowAllData
Set rngFilt = ws.Range("A1").CurrentRegion: ' Debug.Print rngFilt.Address: Stop
arrFilt = rngFilt.Value2 'place the range in an array for faster iteration
'extract the unique Company Names:
Set dictC = CreateObject("Scripting.Dictionary")
For i = 2 To UBound(arrFilt)
If arrFilt(i, 1) <> "" Then
dictC(arrFilt(i, 1)) = dictC(arrFilt(i, 1)) + 1
End If
Next i
Application.ScreenUpdating = False 'optimization to make code faster
Dim keyC As Variant, rngF As Range, rngF1 As Range
For Each keyC In dictC.Keys 'iterate between dictionary keys (A:A company names)
rngFilt.AutoFilter 1, keyC 'first filter by dict key
rngFilt.AutoFilter 4, arrCust, xlFilterValues 'second by array of customers numbers
Set wsComp = Nothing
'insert the necessary sheets, name them (if not existing), clear if existing and copy the filtered range
Application.EnableEvents = False: Application.Calculation = xlCalculationManual
Application.AutomationSecurity = msoAutomationSecurityForceDisable
On Error Resume Next
Set wsComp = wb.Worksheets(keyC)
On Error GoTo 0
If Not wsComp Is Nothing Then
wsComp.Cells.ClearContents
Else
Set wsComp = wb.Worksheets.Add(After:=ws)
wsComp.Name = keyC
End If
rngFilt.Rows(1).Copy ' copy the headers columns width
wsComp.Range("A1").Resize(, rngFilt.Rows(1).Columns.Count).PasteSpecial xlPasteColumnWidths
On Error Resume Next
Set rngF1 = Nothing
Set rngF1 = rngFilt.Resize(rngFilt.Rows.Count - 1).Offset(1).SpecialCells(xlCellTypeVisible):
Set rngF = rngFilt.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If Not rngF1 Is Nothing Then
rngF.Copy wsComp.Range("A1")
InsertHeaders wsComp
Else
Application.DisplayAlerts = False
wb.Worksheets(keyC).Delete
Application.DisplayAlerts = True
End If
'ws.ShowAllData
Application.AutomationSecurity = msoAutomationSecurityByUI
Application.EnableEvents = True: Application.Calculation = xlCalculationAutomatic
Next keyC
Application.ScreenUpdating = True
ws.AutoFilterMode = False
'MsgBox "Ready..."
Delete
End Sub
IS that correct or do you have any other recommendations
Please, copy the next solution to a standard module (instead of the existing code):
Option Explicit
Sub CopyFilteredCustomersByCompanyNames()
Dim wb As Workbook, ws As Worksheet, lastR As Long, wsComp As Worksheet, dictC As Object
Dim rngFilt As Range, arrCust() As Variant, arrFilt, i As Long
arrCust = Array("108169651", "108169651", "108169430", "108169430", "108168704", "108169596") 'place here the 10 specific customers name
Set wb = ActiveWorkbook 'use here the workbook you need
Set ws = ActiveSheet 'use here the necessary sheet (the one to be processed)
If ws.FilterMode Then ws.ShowAllData
Set rngFilt = ws.Range("A1").CurrentRegion: ' Debug.Print rngFilt.Address: Stop
arrFilt = rngFilt.Value2 'place the range in an array for faster iteration
'extract the uneque Company Names:
Set dictC = CreateObject("Scripting.Dictionary")
For i = 2 To UBound(arrFilt)
If arrFilt(i, 1) <> "" Then
dictC(arrFilt(i, 1)) = dictC(arrFilt(i, 1)) + 1
End If
Next i
Application.ScreenUpdating = False 'optimization to make code faster
Dim keyC As Variant, rngF As Range, rngF1 As Range
For Each keyC In dictC.Keys 'iterate between dictionary keys (A:A company names)
rngFilt.AutoFilter 1, keyC 'first filter by dict key
rngFilt.AutoFilter 4, arrCust, xlFilterValues 'second by array of customers numbers
Set wsComp = Nothing
'insert the necessary sheets, name them (if not existing), clear if existing and copy the filtered range
Application.EnableEvents = False: Application.Calculation = xlCalculationManual
Application.AutomationSecurity = msoAutomationSecurityForceDisable
On Error Resume Next
Set wsComp = wb.Worksheets(keyC)
On Error GoTo 0
If Not wsComp Is Nothing Then
wsComp.Cells.ClearContents
Else
Set wsComp = wb.Worksheets.Add(After:=ws)
wsComp.Name = keyC
End If
rngFilt.Rows(1).Copy ' copy the headers columns width
wsComp.Range("A1").Resize(, rngFilt.Rows(1).Columns.Count).PasteSpecial xlPasteColumnWidths
On Error Resume Next
Set rngF1 = Nothing
Set rngF1 = rngFilt.Resize(rngFilt.Rows.Count - 1).Offset(1).SpecialCells(xlCellTypeVisible):
Set rngF = rngFilt.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If Not rngF1 Is Nothing Then
rngF.Copy wsComp.Range("A1")
InsertHeaders wsComp
Else
Application.DisplayAlerts = False
wb.Worksheets(keyC).Delete
Application.DisplayAlerts = True
End If
ws.ShowAllData
Application.AutomationSecurity = msoAutomationSecurityByUI
Application.EnableEvents = True: Application.Calculation = xlCalculationAutomatic
Next keyC
Application.ScreenUpdating = True
MsgBox "Ready..."
End Sub
Sub InsertHeaders(ws As Worksheet)
Dim rngSub As Range, lastR As Long, firstAddress As String, rngUnion As Range
Dim i As Long, dict As Object
'check if more than one unique Account exists:
lastR = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Set dict = CreateObject("Scripting.Dictionary")
For i = 2 To lastR
dict(ws.Cells(i, 4).Value) = vbNullString
Next i
If dict.Count < 2 Then Exit Sub 'for only one customer code, no need of other headers...
'sort the range:
ws.UsedRange.Sort key1:=ws.UsedRange.Cells(1, 4), Order1:=xlAscending, Header:=xlYes
'place Subtotals based on Account (customer number):
ws.UsedRange.Subtotal GroupBy:=4, Function:=xlSum, TotalList:=Array(12), _
Replace:=True, PageBreaks:=False, SummaryBelowData:=True
'delete last two rows:
lastR = ws.Cells(ws.Rows.Count, 4).End(xlUp).Row
ws.Rows(lastR - 1 & ":" & lastR).Delete: 'Stop
'place all cells containing "SUBTOTAL'" in formula in a Union Range:
Set rngSub = ws.UsedRange.Columns(12).Find(What:="SUBTOTAL", After:=ws.UsedRange.Columns(12).Cells(1), LookIn:=xlFormulas, lookat:=xlPart)
If rngSub Is Nothing Then Exit Sub
firstAddress = rngSub.Address
addToRange rngUnion, rngSub
Do
Set rngSub = ws.UsedRange.FindNext(rngSub)
addToRange rngUnion, rngSub
Loop While rngSub.Address <> firstAddress
'copy the header row to the places of Subtotals rows:
With ws.Rows("1:1")
.VerticalAlignment = xlCenter
.Copy rngUnion.EntireRow 'copy the header in all Union range
End With
'remove Subtotals (needed only temporary):
ws.UsedRange.RemoveSubtotal
End Sub
Sub addToRange(rngU As Range, rng As Range) 'sub adding the new range to a Union one...
If rngU Is Nothing Then
Set rngU = rng
Else
Set rngU = Union(rngU, rng)
End If
End Sub
Your existing code has only one modification: The new sub call:
rngF.Copy wsComp.Range("A1")
InsertHeaders wsComp
instead of
rngF.Copy wsComp.Range("A1")
Please, send some feedback after testing it.

Highlight intersection cell of row and column based on Text matching using VBA

I am trying to use VBA by which when the text in a column header is the same as the text in a row the intersection cell of the row and the column gets highlighted with some Color.
Example: I tried with below Code but not giving the required output
Sub cellintersection()
Application.EnableEvents = False
Application.ScreenUpdating = False
Dim ws As Worksheet
Set ws = ActiveSheet
Dim cols As Range, rws As Range
Dim lastRow As Integer: lastRow = ws.UsedRange.Rows.Count
Dim lastColumn As Integer: lastColumn = ws.UsedRange.Columns.Count
For Each cols In ws.Range(ws.Cells(1, 1), ws.Cells(1, lastColumn))
If (Not (cols.Value = vbNullString)) Then
For Each rws In ws.Range("A1:A" & lastRow)
If (rws.Value = cols.Value) Then ws.Cells(rws.Row, cols.Column).Interior.Color = 5296210
Next
End If
Next
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
Required Output: The cells with green by matching the text with blue.
So with conditional formatting as per my comment:
Select range B4:D6
Start > Conditional Formatting > New Rule > Formula:
=B$2=$A4
Choose your fill color and confirm
Notice, filling cells through VBA is static while conditional formatting is dynamic and will change according to changes made to your data.
I fixed some errors I found:
Sub cellintersection()
Application.EnableEvents = False
Application.ScreenUpdating = False
Dim ws As Worksheet
Set ws = ActiveSheet
Dim cols As Range, rws As Range
Dim lastRow As Integer: lastRow = ws.UsedRange.Rows.Count
Dim lastColumn As Integer: lastColumn = ws.UsedRange.Columns.Count
For Each cols In ws.Range(ws.Cells(2, 1), ws.Cells(2, lastColumn))
If cols.Value <> vbNullString Then
For Each rws In ws.Range("A1:A" & lastRow)
If rws.Value = cols.Value Then ws.Cells(rws.Row, cols.Column).Interior.Color = 5296210
Next
End If
Next
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
When starting the first For...Each Loop you are looking through row 1, which does not have any values in it. Your headers are in row 2. Also some of your If statements were unneccesarily complicated, for example
If (Not (cols.Value = vbNullString)) Then
is the same as
If cols.Value <> vbNullString Then

Error 438"Object Doesn't Support This Property or Method"

Related to Excel VBA - I have a large dataset and would like to split it by Ratings. For a small dataset the code works perfectly, but for a large dataset (11,000 rows & 20 columns), it loops and either get "Restart Excel program" or a 438 error. Need some help to optimize/correct the code. Using Excel 2013
I tried Cut/paste instead of copy/paste - it does not work
Private Sub SplitData_Click()
a = Sheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To a
If Sheets("Sheet1").Cells(i, 2).Value = "AAA" Then
Sheets("Sheet1").Rows(i).Cut
Sheets("Sheet2").Activate
b = Sheets("Sheet2").Cells(Rows.Count, 1).End(xlUp).Row
Sheets("Sheet2").Cells(b + 1, 1).Select
ActiveSheet.Paste
End If
If Sheets("Sheet1").Cells(i, 2).Value = "BBB" Then
Sheets("Sheet1").Rows(i).Cut
Sheets("Sheet3").Activate
c = Sheets("Sheet3").Cells(Rows.Count, 1).End(xlUp).Row
Sheets("Sheet3").Cells(c + 1, 1).Select
ActiveSheet.Paste
End If
If Sheets("Sheet1").Cells(i, 2).Value = "CCC" Then
Sheets("Sheet1").Rows(i).Cut
Sheets("Sheet4").Activate
d = Sheets("Sheet4").Cells(Rows.Count, 1).End(xlUp).Row
Sheets("Sheet4").Cells(d + 1, 1).Select
ActiveSheet.Paste
End If
Sheets("Sheet1").Activate
Next
Application.CutCopyMode = False
End Sub
I want to split the large data set into different groups (Sheets) based on the value - AAA, BBB or CCC. I have 10 such value flags.
Another approach:
Private Sub SplitData_Click()
Dim a As Long, i As Long, sht As Worksheet, sDest As String
Set sht = Sheets("Sheet1")
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
a = sht.Cells(sht.Rows.Count, 1).End(xlUp).Row
For i = a To 2 Step -1 'work from bottom up
sDest = ""
'need to cut this row?
Select Case sht.Cells(i, 2).Value
Case "AAA": sDest = "Sheet2"
Case "BBB": sDest = "Sheet3"
Case "CCC": sDest = "Sheet4"
End Select
'cut row to relevant sheet
If Len(sDest) > 0 Then
sht.Rows(i).Cut Sheets(sDest).Cells(Rows.Count, 1).End(xlUp).Offset(1, 0)
End If
Next i
Application.CutCopyMode = False
Application.Calculation = xlCalculationAutomatic
End Sub
NOTE: locating the "cut to" cell using xlUp relies on every previous row in the destination sheet having a value in ColA - if any are empty then rows could get overwritten by the next pasted row.
Try this. This should be faster as this doesn't involve ANY looping.
Logic
Use Autofilter to Copy the rows across in one go
Clear rows after copying
Delete blank rows in one go using Autofilter
Code
Dim wsInput As Worksheet
Sub SplitData_Click()
Dim wsOutputA As Worksheet
Dim wsOutputB As Worksheet
Dim wsOutputC As Worksheet
Set wsInput = ThisWorkbook.Sheets("Sheet1")
Set wsOutputA = ThisWorkbook.Sheets("Sheet2")
Set wsOutputB = ThisWorkbook.Sheets("Sheet3")
Set wsOutputC = ThisWorkbook.Sheets("Sheet4")
Dim lrow As Long
Dim rng As Range
With wsInput
.AutoFilterMode = False
lrow = .Range("A" & .Rows.Count).End(xlUp).Row
Set rng = .Range("A1:A" & lrow)
'~~> Filter on AAA
HandleIt "AAA", rng, wsOutputA
'~~> Filter on BBB
HandleIt "BBB", rng, wsOutputB
'~~> Filter on CCC
HandleIt "CCC", rng, wsOutputC
'~~> Filter on blanks
With rng
.AutoFilter Field:=1, Criteria1:="="
.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
.AutoFilterMode = False
End With
End Sub
Private Sub HandleIt(AFCrit As String, r As Range, wks As Worksheet)
Dim OutputRow As Long
Dim filteredRange As Range
With r
.AutoFilter Field:=1, Criteria1:=AFCrit
Set filteredRange = .Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow
End With
If Not filteredRange Is Nothing Then
With wks
OutputRow = .Range("A" & .Rows.Count).End(xlUp).Row + 1
filteredRange.Copy .Rows(OutputRow)
filteredRange.ClearContents
End With
End If
wsInput.ShowAllData
End Sub
In Action
Note: The above code took 4 seconds on 21k rows x 31 columns data
Please see How to avoid using Select in Excel VBA.
Option Explicit
Private Sub SplitData_Click()
Dim i As Long
With Worksheets("Sheet1")
For i = 2 To .Cells(.Rows.Count, 1).End(xlUp).Row
Select Case .Cells(i, 2).Value
Case "AAA"
MoveToEndOf .Rows(i), Worksheets("Sheet2")
Case "BBB"
MoveToEndOf .Rows(i), Worksheets("Sheet3")
Case "CCC"
MoveToEndOf .Rows(i), Worksheets("Sheet4")
End Select
Next
End With
End Sub
Private Sub MoveToEndOf(ByVal what As Range, ByVal where As Worksheet)
what.Cut where.Cells(where.Rows.Count, 1).End(xlUp).Offset(1, 0)
End Sub
Here is an option without using copy/paste
Private Sub SplitData_Click()
Dim a As Long
Dim b As Long
Dim c As Long
Dim d As Long
Dim i As Long
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim ws3 As Worksheet
Dim ws4 As Worksheet
Set ws1 = ThisWorkbook.Sheets("Sheet1")
Set ws2 = ThisWorkbook.Sheets("Sheet2")
Set ws3 = ThisWorkbook.Sheets("Sheet3")
Set ws4 = ThisWorkbook.Sheets("Sheet4")
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
a = ws1.Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To a
If ws1.Cells(i, 2).Value = "AAA" Then
b = ws2.Cells(Rows.Count, 1).End(xlUp).Row + 1
ws2.Rows(b).Value = ws1.Rows(i).Value
End If
If ws1.Cells(i, 2).Value = "BBB" Then
c = Sheets("Sheet3").Cells(Rows.Count, 1).End(xlUp).Row + 1
ws2.Rows(c).Value = ws1.Rows(i).Value
End If
If ws1.Cells(i, 2).Value = "CCC" Then
d = Sheets("Sheet4").Cells(Rows.Count, 1).End(xlUp).Row + 1
ws2.Rows(d).Value = ws1.Rows(i).Value
End If
Next i
With Application
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
End With
End Sub

Speed up copying values

This code takes 8 cells from a data entry form and copies those cells to the next empty row on another worksheet that is used as a database. It takes 15 seconds. It can speed up the code if it didn't copy to another sheet.
Is there a way to significantly speed up this code without merging the two sheets?
sub UpdateLogWorksheet1()
Application.ScreenUpdating = False
Application.EnableEvents = False
Dim historyWks As Worksheet
Dim inputWks As Worksheet
Dim nextRow As Long
Dim oCol As Long
Dim myRng As Range
Dim myCopy As String
Dim myclear As String
Dim myCell As Range
ActiveSheet.Unprotect "sallygary"
myCopy = "e4,g26,g16,g12,g18,g20,g22,g24"
Set inputWks = Worksheets("Dept 1 Input")
Set historyWks = Worksheets("1_Data")
With historyWks
nextRow = .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0).Row
End With
With inputWks
Set myRng = .Range(myCopy)
End With
With historyWks
With .Cells(nextRow, "A")
.Value = Now()
.NumberFormat = "mm/dd/yyyy"
End With
.Cells(nextRow, "B").Value = Application.UserName
oCol = 3
For Each myCell In myRng.Cells
historyWks.Cells(nextRow, oCol).Value = myCell.Value
oCol = oCol + 1
Next myCell
End With
With inputWks
On Error Resume Next
End With
On Error GoTo 0
ActiveSheet.Protect "sallygary"
Range("g12").Select
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Don't copy cell by cell. Copy Entire tables with one operation. For example to copy a 100×3 table
Sheet2.Range("A2").Resize(100,3).Value2 = Sheet1.Range("G2").Resize(100,3).Value2

Resources