Delete rows by using criteria
I often need to delete rows in a table, if values in a certain column are higher or lower than something.
It could be records/rows where a machine isn't running, or where the production is lower than x tonnes per day - there can be many reasons.
You can do this manually by sorting the data from e.g. lowest to highest, but it is easier and faster to use VBA macros. On this page I show how to do just that.
The macros use Ranges, Arrays, UserForms (also modeless), a ListBox, loops, a function that returns True or False - and more. Criteria for deletion must be numeric values, but that could be changed without too much trouble.
It is probably important to say that it is not the entire row that is deleted - it is only the row in the table, and rows below and outside the table will not change position.
I usually work with process data imported from text files (or csv), so I run the macro from one workbook and operate on the other workbook with the data, I need to analyze.
You can copy the VBA code from this page, but it is easier to download a zip-compressed workbook with the macros and UserForms: Click here to download.
The company Webucator, that offers VBA classes, has uploaded a video to YouTube showing how these macros work.
The video isn't educational in the sense that it explains the VBA code in detail (see my comments in the code below for that), but you can see how it performs.
Here we go. First we declare some variables at the module's top, and then we see the start procedure.
Option Explicit
Public bSmaller As Boolean
Public bEquals As Boolean
Public bGreater As Boolean
Public bAbort As Boolean
Public lSelCol As Long
Public dSortVal As Double
Sub OpenSort()
Dim vInput
On Error GoTo ErrorHandle
vInput = MsgBox("Removes rows in a table, if values in a column" _
& vbNewLine & _
"meets a user defined condition, like " & vbNewLine & _
"e.g. less than a certain value. " & vbNewLine & _
"Beware that numbers in the table can be rounded.", _
vbOKCancel, "Remove rows")
If vInput = vbCancel Then Exit Sub
If Workbooks.Count = 1 Then
MsgBox "You need to open the workbook containing data."
Exit Sub
ElseIf Workbooks.Count = 2 Then
If Workbooks(Workbooks.Count).Name = ThisWorkbook.Name Then
Workbooks(1).Activate
Else
Workbooks(Workbooks.Count).Activate
End If
Criteria
Else
Workbooks(Workbooks.Count).Activate
With frmPickSheet
.StartUpPosition = 3
.Show vbModeless
End With
End If
Exit Sub
ErrorHandle:
MsgBox Err.Description & " Procedure OpenSort"
End Sub
Towards the end of the procedure we open a UserForm (frmPickSheet), if there are more than 2 open workbooks.
Normally a UserForm has focus, and you can't work outside the form before it is closed, but by adding "vbModeless" the form just "hovers," and the user can do other things like (in this case) activating the workbook and sheet with the data.
In the spreadsheet, you can download, it looks like this:
The UserForm has but little code, and "cmdOK" is the name of the OK-button:
Private Sub cmdOK_Click()
On Error GoTo ErrorHandle
Unload Me
Criteria
Exit Sub
ErrorHandle:
MsgBox Err.Description
End Sub
You can see the Criteria procedure below. First it shows a UserForm, frmSelectColumn, with a ListBox, where the user must select the column that contains the values that determine which rows to delete.
After that it shows another UserForm, where the user must define the criteria for deleting rows - e.g. values < 4000.
The next step is to copy the table to an array, MyArray. Here we loop through the selected column, and all rows except those marked for deletion are copied to another array, NewArray. The old table with data is replaced with the new one in NewArray.
Let's go.
Sub Criteria()
Dim bDelete As Boolean
Dim MyArray() As Variant
Dim NewArray() As Variant
Dim rCell As Range
Dim rTable As Range
Dim lRows As Long
Dim lCols As Long
Dim lCount As Long
Dim lCount2 As Long
Dim lCount3 As Long
Dim lDelete As Long
Dim lStartRow As Long
On Error GoTo ErrorHandle
If Len(Range("A1").Value) = 0 Then
MsgBox "The table must start in cell A1."
Exit Sub
End If
frmSelectColumn.Show
If bAbort Then
bAbort = False
GoTo BeforeExit
End If
frmCriteria.Show
If bAbort Then
bAbort = False
GoTo BeforeExit
End If
Application.ScreenUpdating = False
Set rTable = Range("A1").CurrentRegion
With rTable
lCols = .Columns.Count
lRows = .Rows.Count
End With
MyArray = rTable.Value
Set rTable = Nothing
For lCount = 1 To lRows
If IsNumeric(MyArray(lCount, lSelCol)) Then
lStartRow = lCount
Exit For
End If
Next
If lStartRow = lRows Then
MsgBox "The column has no numeric values."
GoTo BeforeExit
End If
For lCount = lStartRow To lRows Step 1
If IsNumeric(MyArray(lCount, lSelCol)) Then
bDelete = DeleteRow(MyArray(lCount, lSelCol))
If bDelete = True Then
MyArray(lCount, 1) = "delete"
lDelete = lDelete + 1
End If
End If
Next
If lDelete = 0 Then
MsgBox "No values met the criterion."
GoTo BeforeExit
End If
ReDim NewArray(1 To lRows - lDelete, 1 To lCols)
For lCount = 1 To lRows
If MyArray(lCount, 1) <> "delete" Then
lCount3 = lCount3 + 1
For lCount2 = 1 To lCols
NewArray(lCount3, lCount2) = MyArray(lCount, lCount2)
Next
End If
Next
Set rTable = Range("A1").CurrentRegion
rTable.ClearContents
Set rTable = Range("A1")
Set rTable = rTable.Resize(UBound(NewArray), lCols)
rTable.Value = NewArray
BeforeExit:
On Error Resume Next
Erase MyArray
Erase NewArray
Set rTable = Nothing
bAbort = False
lSelCol = 0
Application.ScreenUpdating = True
Exit Sub
ErrorHandle:
MsgBox Err.Description & " Procedure Criteria"
Resume BeforeExit
End Sub
On the way we showed 2 UserForms, frmSelectColumn and frmCriteria, to get user input.
In the spreadsheet for download they look like this. First frmSelectColumn with code.
The code finds the headers (if any) in the first row. Below the code in frmSelectColumn.
Private Sub UserForm_Initialize()
Dim rCell As Range
Dim rRow As Range
If IsEmpty(Range("A1")) Then
MsgBox "Cell A1 is empty. The macro stops."
bAbort = True
Unload Me
End If
If IsEmpty(Range("B1")) = False Then
Set rRow = Range(Range("A1"), Range("A1").End(xlToRight))
For Each rCell In rRow
ListBox1.AddItem rCell.Value
Next
Else
ListBox1.AddItem Range("A1").Value
End If
BeforeExit:
Set rCell = Nothing
Set rRow = Nothing
Exit Sub
ErrorHandle:
MsgBox Err.Description & " Procedure UserForm_Initialize"
Resume BeforeExit
End Sub
Private Sub CommandButton1_Click()
With ListBox1
If .ListIndex = -1 Then
MsgBox "You must select a column."
Else
lSelCol = .ListIndex + 1
Unload Me
End If
End With
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = vbFormControlMenu Then
CommandButton2_Click
End If
End Sub
Private Sub CommandButton2_Click()
bAbort = True
Unload Me
End Sub
The UserForm for defining deletion criteria looks like this in my example:
and has the following code:
Private Sub UserForm_Initialize()
OptionButton1.Value = True
TextBox1.SetFocus
End Sub
Private Sub OptionButton1_Click()
TextBox1.SetFocus
End Sub
Private Sub OptionButton2_Click()
TextBox1.SetFocus
End Sub
Private Sub OptionButton3_Click()
TextBox1.SetFocus
End Sub
Private Sub TextBox1_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
Select Case KeyAscii
Case 44 To 57
Case Else
KeyAscii = 0
End Select
End Sub
Private Sub CommandButton1_Click()
With TextBox1
If Len(.Text) > 0 Then
dSortVal = CDbl(.Text)
Else
MsgBox "You must write a value."
Exit Sub
End If
End With
If OptionButton1.Value = True Then
bSmaller = True
bEquals = False
bGreater = False
End If
If OptionButton2.Value = True Then
bEquals = True
bSmaller = False
bGreater = False
End If
If OptionButton3.Value = True Then
bGreater = True
bSmaller = False
bEquals = False
End If
Unload Me
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = vbFormControlMenu Then
bAbort = True
Unload Me
End If
End Sub
The last thing is the Boolean function, DeleteRow.
Function DeleteRow(ByVal dVal As Double) As Boolean
If bSmaller Then
If dVal < dSortVal Then
DeleteRow = True
Exit Function
Else
DeleteRow = False
Exit Function
End If
End If
If bEquals Then
If dVal = dSortVal Then
DeleteRow = True
Exit Function
Else
DeleteRow = False
Exit Function
End If
End If
If bGreater Then
If dVal > dSortVal Then
DeleteRow = True
Exit Function
Else
DeleteRow = False
Exit Function
End If
End If
End Function
That was it. You could make it possible to add more criteria, but the macro is actually pretty fast, so you can just run it again for deleting more rows.
You can copy-paste the code on this page, but it is easier to download the sample spreadsheet.
Related:
|