Arrays and ranges in Excel VBA - delete rows
Many, who start using VBA in Excel, quickly learn how to use ranges, because they are really handy and relatively easy to understand and use.
This page shows examples of how you can speed things up a lot by using arrays. This could be deleting rows in a range (table) or other operations.
You CAN loop through a range and remove or insert e.g. rows, but it is tricky, because your range suddenly changes, and if for instance you remove the row you are in, you will suddenly be in the row that was the row below just a moment ago! It is both easier and faster with an array.
The first example shows how to remove every second row, and the next how to delete rows going from the last row upwards, if the row above has identical/duplicate values in selected columns.
You can use other criteria, and the task doesn't have to be the removal of rows - that is just the objective for my examples. You can also download a spreadsheet with the examples.
The advantage of using arrays
If you work with big tables/ranges, you can usually speed up the code a lot, if you copy the range to an array. Once you have finished operating on the array, you copy the array to the spreadsheet in one lightning fast operation.
You get the speed increase, because there is a large overhead writing to and from a range in a spreadsheet, whereas it is very fast to operate on a virtual table (the array).
The method isn't suitable for all tasks, but I think that many people work with very big tables in Excel (e.g. log files or reports) and often need to "weed" the data using certain criteria.
If the task is sorting or filtering, you should always use Excel's built in functions (e.g. auto filtering), because no VBA code will ever match the speed of those; but if you have special needs, VBA can be a great help.
Delete every second row
The following example is fairly simple: It removes every second row in a range. I have no idea if anybody ever needs to do this, but because it is simple, it is good for showing the technique.
- Copy the range to a two-dimensional array in one (very fast)operation. The array will automatically get the same dimensions as the range (number of rows and columns)
- Loop through the array and copy the chosen records (rows - in this case every second) to a new array.
- Insert the new array as a table in the spreadsheet in one fast operation - no slow looping writing cell by cell.
For the example to work the sheet with the table must be the active sheet, and the table must start in cell A1. You can highlight the code with the mouse, copy it (CTRL+C) and paste it into a VBA module with CTRL+V. You can also download a spreadsheet with the examples.
Sub DeleteEverySecondRow()
Dim MyArray() As Variant
Dim NewArray() As Variant
Dim rTable As Range
Dim lCount As Long
Dim lCount2 As Long
Dim lCount3 As Long
Dim lRows As Long
Dim lCols As Long
Dim lNewRows As Long
On Error GoTo ErrorHandle
Application.ScreenUpdating = False
If Len(Range("A1")) = 0 Then
MsgBox "Celle A1 is empty. For the example to work " & _
"the table must start in cell A1."
GoTo BeforeExit
End If
Set rTable = Range("A1").CurrentRegion
MyArray = rTable.Value
With rTable
lRows = .Rows.Count
lCols = .Columns.Count
End With
If lRows Mod 2 = 0 Then
lNewRows = lRows / 2
Else
lNewRows = lRows / 2 + 1
End If
ReDim NewArray(lNewRows, lCols)
For lCount = 1 To lRows Step 2
lCount2 = lCount2 + 1
For lCount3 = 1 To lCols
NewArray(lCount2, lCount3) = MyArray(lCount, lCount3)
Next
Next
Sheets.Add
Set rTable = Range("A1").Resize(lNewRows, lCols)
rTable.Value = NewArray
BeforeExit:
On Error Resume Next
Set rTable = Nothing
Application.ScreenUpdating = True
Exit Sub
ErrorHandle:
MsgBox Err.Description & " Error in procedure DeleteEverySecondRow."
Resume BeforeExit
End Sub
Delete identical rows in a table
The next example is more complex, but probably more useful! It shows how you can delete rows by using certain criteria.
I use a very similar macro at work to "weed" daily reports with chemical analyses. Not all products are analysed every hour, and if there isn't a new analysis on the hour, the last one will be repeated in the report.
The daily report has 24 rows, one for every hour, and in my tables there can be data for maybe a year.
So to have unique analyses only I want to remove the rows that are mere repetitions of the row above. If for instance a production unit is stopped, the same analysis will be there for days, and that is not clever, if you want to find standard deviation and stuff like that.
This is what happens:
- The table is defined as a range (here: rTable). The example requires that the table is on the active sheet and starts in cell A1.
- The range is copied to the array, MyArray, in one fast operation.
- The user is asked to select the column(s) that must be compared.
- We then loop through the array from bottom to top, row by row, and if a row is a copy, we insert an identifier (in this case the word "delete") in what matches the first cell in the row.
- A new array, NewArray, is defined.
- All items (rows) in MyArray that do not contain the word "delete" in the first column are copied to NewArray.
- The new array with the "weeded" table is inserted on a new sheet in one, fast operation.
Here we go:
Sub RemoveDuplicates()
Dim bSame As Boolean
Dim MyArray() As Variant
Dim NewArray() As Variant
Dim rCell As Range
Dim rTable As Range
Dim rIsect As Range
Dim lRows As Long
Dim lCols As Long
Dim lCount As Long
Dim lCount2 As Long
Dim lCount3 As Long
dim lDel as Long
Dim colColumns As Collection
On Error GoTo ErrorHandle
If Len(Range("A1")) = 0 Then
MsgBox "Cell A1 is empty. The example requires " & _
"that the table begins in cell A1."
GoTo BeforeExit
End If
Set rTable = Range("A1").CurrentRegion
MyArray = rTable.Value
On Error Resume Next
Set rCell = Application.InputBox(prompt:="Select the columns " & _
" you want to compare. Selecting one cell per column " & _
"is enough.", Type:=8)
If rCell Is Nothing Then
MsgBox "You didn't select any column - the program stops."
GoTo BeforeExit
End If
On Error GoTo ErrorHandle
Application.ScreenUpdating = False
Set rIsect = Application.Intersect(rCell, rTable)
If rIsect Is Nothing Then
MsgBox "The selected area is not in the table"
GoTo BeforeExit
End If
Set colColumns = New Collection
With rTable
For lCount = 1 To .Columns.Count
Set rIsect = Application.Intersect(rCell, .Columns(lCount))
If Not rIsect Is Nothing Then
colColumns.Add lCount
End If
Next
End With
With rTable
lRows = .Rows.Count
lCols = .Columns.Count
End With
lDel = 0
For lCount = lRows To 2 Step -1
bSame = True
With colColumns
For lCount2 = 1 To .Count
If MyArray(lCount, .Item(lCount2)) <> _
MyArray(lCount - 1, .Item(lCount2)) Then
bSame = False
Exit For
End If
Next
If bSame Then
MyArray(lCount, 1) = "delete"
lDel = lDel + 1
End If
End With
Next
lCount2 = 0
lDel = lRows - lDel
ReDim NewArray(lDel, lCols)
For lCount = LBound(MyArray) To UBound(MyArray)
If MyArray(lCount, 1) <> "delete" Then
lCount2 = lCount2 + 1
For lCount3 = 1 To lCols
NewArray(lCount2, lCount3) = MyArray(lCount, lCount3)
Next
End If
Next
Sheets.Add
Set rCell = Range("A1").Resize(lDel, lCols)
rCell.Value = NewArray
BeforeExit:
Set rCell = Nothing
Set rTable = Nothing
Set rIsect = Nothing
Set colColumns = Nothing
Application.ScreenUpdating = True
Exit Sub
ErrorHandle:
MsgBox Err.Description & " Error in procedure RemoveDuplicates."
Resume BeforeExit
End Sub
To top
Related:
|