arrays - 删除使用 union 以外的一系列行的更快方法

标签 arrays excel vba union

我正在使用以下代码:
删除相似的行,只保留一个并合并“N”范围内的单元格值,由 vbLf 分隔
它有效,但范围很大(例如 3 万行),宏需要很长时间才能完成。
调试代码后,我发现使用 union 会导致宏需要很长时间才能完成。

Set rngDel = Union(rngDel, ws.Range("A" & i + m))

那么对于下面的代码,如何采用一种更快的方法来删除使用 union 以外的行范围?
预先感谢任何有用的评论和答案。

Sub DeleteSimilarRows_combine_Last_Column_N()
 
    Dim LastRow As Long, ws As Worksheet, arrWork, rngDel As Range, i As Long, j As Long, k As Long
     Dim strVal As String, m As Long
 
      Set ws = ActiveSheet: LastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
    arrWork = ws.Range("A1:A" & LastRow).Value2 'Place the range in an array to make iteration faster
 
    Application.DisplayAlerts = False: Application.ScreenUpdating = False
    For i = 2 To UBound(arrWork) - 1                'Iterate between the array elements:
        If arrWork(i, 1) = arrWork(i + 1, 1) Then
            'Determine how many consecutive similar rows exist:______
            For k = 1 To LastRow
                If i + k + 1 >= UBound(arrWork) Then Exit For
                If arrWork(i, 1) <> arrWork(i + k + 1, 1) Then Exit For
            Next k '__
 
            For j = 14 To 14                  'Build the concatenated string of cells in range "N":
                strVal = ws.Cells(i, j).Value
                For m = 1 To k
                    strVal = strVal & vbLf & ws.Cells(i + m, j).Value
                Next m
                ws.Cells(i, j).Value = strVal: strVal = ""
           Next j
 
           For m = 1 To k                    'Place the cells for rows to be deleted in a Union range, to delete at the end, at once
                If rngDel Is Nothing Then
                     Set rngDel = ws.Range("A" & i + m)
                Else
                    Set rngDel = Union(rngDel, ws.Range("A" & i + m)) 'This line causes macro takes very long time to finish.
                End If
         Next m
         i = i + k: If i >= UBound(arrWork) - 1 Then Exit For 'Increment the i variable and exiting if the resulted value exits the array size
       End If
    Next i
 
    If Not rngDel Is Nothing Then rngDel.EntireRow.Delete    'Delete the not necessary rows
    Application.DisplayAlerts = True: Application.ScreenUpdating = True
End Sub

最佳答案

随着您向范围中添加更多单元格/区域,并集会逐渐变慢(参见此处的数字:https://stackoverflow.com/a/56573408/478884)。如果您是“自下而上”工作,则可以每(例如)500 行删除一次 rngDel,但您不能采用这种方法,因为您是自上而下工作。

这是一种不同的方法 - 将单元格添加到集合中,然后在最后使用批量删除过程“自下而上”地处理集合。

Sub TestRowDeletion()

    Dim rngRows As Range, data, rngDel As Range, i As Long
    Dim t, nRows As Long, colCells As New Collection
    
    Set rngRows = Range("A1:A10000") '10k rows for testing
    
    'Approach #1 - your existing method
    DummyData rngRows     'populate some dummy data
    data = rngRows.Value
    t = Timer
    For i = 1 To UBound(data, 1)
        'removing ~25% of cells...
        If data(i, 1) > 0.75 Then BuildRange rngDel, rngRows.Cells(i)
    Next i
    If Not rngDel Is Nothing Then rngDel.EntireRow.Delete
    Debug.Print "Regular single delete", Timer - t

    'Approach #2 - batch-deleting rows
    DummyData rngRows 'reset data
    data = rngRows.Value
    t = Timer
    For i = 1 To UBound(data, 1)
        If data(i, 1) > 0.75 Then colCells.Add rngRows.Cells(i)
    Next i
    RemoveRows colCells
    Debug.Print "Batch-deleted", Timer - t

    'Approach #3 - array of "delete" flags plus SpecialCells()
    DummyData rngRows 'reset data
    data = rngRows.Value
    t = Timer
    ReDim flags(1 To UBound(data, 1), 1 To UBound(data, 2))
    For i = 1 To UBound(data, 1)
        If data(i, 1) > 0.75 Then
            flags(i, 1) = "x"
            bDelete = True 'flag we have rows to delete
        End If
    Next i
    If bDelete Then
        With rngRows.Offset(0, 10) 'use an empty column....
            .Value = flags  'populate with flags for deletion
            .SpecialCells(xlCellTypeConstants).EntireRow.Delete
        End With
    End If
    Debug.Print "Specialcells", Timer - t

End Sub

'Delete the row for any cell in `col`
'  cells were added to `col` in a "top down" order
Sub RemoveRows(col As Collection)
    Dim rngDel As Range, n As Long
    For n = col.Count To 1 Step -1 'working from the bottom up...
        BuildRange rngDel, col(n)
        If n Mod 250 = 0 Then
            rngDel.EntireRow.Delete
            Set rngDel = Nothing
        End If
    Next n
    If Not rngDel Is Nothing Then rngDel.EntireRow.Delete
End Sub

Sub DummyData(rng As Range)
    With rng
        .Formula = "=RAND()"
        .Value = .Value
    End With
End Sub

Sub BuildRange(ByRef rngTot As Range, rngAdd As Range)
    If rngTot Is Nothing Then
        Set rngTot = rngAdd
    Else
        Set rngTot = Application.Union(rngTot, rngAdd)
    End If
End Sub

时间(秒)- 请注意随着添加更多行,单一删除和批量删除方法的扩展方式有何不同。

# of rows deleted         ~2.5k/10k    ~5k/20k     ~7.5k/30k 
------------------------------------------------------------
1. Regular single delete     10.01         65.9       226
2. Batch-deleted             2.2           4.7        7.8
3. SpecialCells              1.6           3.1        4.7

您还可以考虑在数据集中填充“删除”标志,然后使用自动筛选/删除可见行方法(编辑:添加为方法 #3)

关于arrays - 删除使用 union 以外的一系列行的更快方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72141217/

相关文章:

javascript - js中的数据在php中序列化成什么格式

AI 游戏的 Java 代码

javascript - 从特定数组元素发送请求后,如何将文本 "Send Request"更改为 Pending?

c# - 在 Excel 电子表格中以编程方式在 C# 中创建复选框

vba - 如何在excel宏中检查activecell是否为A1?

php - 12 小时时钟数组排序

VBA - 无法关闭excel

python - 如何在不删除旧数据的情况下用excel中的xlsxwriter写入现有文件

json - 将 JSON 日期转换为 MM/DD/YYYY 格式?

arrays - 使用 VBA(线性方程组)的矩阵数学