【问题标题】:Deleting Rows via For-Loop (within a multi Area Range)通过 For-Loop 删除行(在多区域范围内)
【发布时间】:2020-02-15 11:22:22
【问题描述】:

通常,我会使用带有行号的 For 循环通过向后工作来删除行,这看起来类似于:

For rowIndex = 10 to 1 Step (-1)
    'delete Row with current row index
    next rowIndex

在指定单元格范围内循环时如何执行此操作?下面的代码显然是行不通的(当执行删除命令时,它将在下一次迭代中跳过一个单元格/行,检查herehere):

Dim someRange as Range
'Note: someRange might be a multi area range (multiple unadjoined cells) within one sheet
Dim singleCell as Range
For each singleCell In SomeRange.Cells
    'check for some condition e.g. based (but not limited to) the singleCell's value
    If condition = True then
        singleCell.EntireRow.Delete
        'Note: the deletion has to be done before the next loop-iteration starts
               'Unfortunately, this makes solutions like working with Union unfeasible
        End If
    Next singleCell

有没有人知道如何在不切换到“Backward-Row-Number-Loop”的情况下使第二个代码块工作(参见第一个代码块)?有没有办法以某种方式将 singleCell 指针/计数器“重置”为新值,以便循环不会跳过下一个单元格/行?或任何其他替代方案(如使 Range-Loop 向后工作等)?任何解决方案的提示都将不胜感激,代码也被剪断,但如果有必要我可以不用。

我必须避免将不需要的行添加到数组(范围等)中,并在循环完成后删除整个数组。不幸的是,作为实现的条件,必须在评估下一个单元格之前删除该行。

基本上可能存在这样一种情况,即应该删除哪些行的两个单元格将位于同一行中。在这种情况下,第二个单元格将在被检查之前被删除,这是理想的行为。解决方案不必但应该可以扩展以包含这种情况。

编辑:我正在考虑的另一个解决方案是在执行 For-Loop (see here) 之前反转范围,但到目前为止我还没有尝试过。

【问题讨论】:

  • 评论不用于扩展讨论;这个对话是moved to chat
  • 您可以使用相同的第一个代码 For rowIndex = 10 to 1 Step (-1) for somerange 也可以作为For rowIndex = somerange.rows.count to 1 Step (-1)
  • @Naresh 谢谢,但这不是一个选项,请参阅我的问题:without switching to a "Backward-Row-Number-Loop"

标签: excel vba


【解决方案1】:

请试试这个代码:

Sub TestdeleteRows()
 Dim sh As Worksheet, someRange As Range, singleCell As Range, rngDel As Range
 Set sh = ActiveSheet ' use what sheet you need
 Set someRange = sh.Range("A2:A20")
 For Each singleCell In someRange.Cells
    If singleCell.Value = Empty Then
        If rngDel Is Nothing Then
            Set rngDel = singleCell
        Else
            Set rngDel = Union(rngDel, singleCell)
        End If
    End If
 Next
 rngDel.EntireRow.Delete xlUp
End Sub

它会删除空单元格的 EntireRow。它可以适应所有需要。

【讨论】:

  • 谢谢,但我已经知道解决方法,不幸的是它对我不可行,正如我在我的问题中所说:“我还想避免将不需要的行添加到数组中并在之后删除它. 由于流程限制,必须在评估下一个单元格之前删除该行。"
  • rngDel 不是一个数组...它是一个Range。但是,没关系。你能定义那些具体的“限制”吗?
  • 在循环迭代中直接删除只是实现的固定规范(请参阅我的问题中的编辑)。
  • 我忘记添加step -1,现在已修复。但是,我的第一个代码是我不想实现它的一个示例,因此它不起作用。据我所知,它不适用于 SomeRange.Cells 中的每个 singleCell,或者至少我无法让它工作。
【解决方案2】:

'如果您根据条件向下删除行,则要评估的下一行的行号将与当前删除的行号相同。

'请尝试关注。

Sub test()

Dim someRange As Range
'Note: someRange might be a multi area range (multiple unadjoined cells) 
within one sheet
Set someRange = Range("B1:B18")
J = someRange.Rows.Count
Dim i As Long
i = 1
Dim singleCell As Long
For singleCell = 1 To someRange.Rows.Count
'check for some condition
If someRange.Cells(singleCell, 1) = 0 Then
    someRange.Cells(singleCell, 1).EntireRow.Delete
    'now as the row is deleted you should reset the value of singleCell
    singleCell = singleCell - 1
    'Note: the deletion has to be done before the next loop-iteration starts
    End If


    i = i + 1
    If i = J Then Exit For
    'someRange.Rows.Count will be the max number of rows to be evaluated
    'otherwise there will be infinite loop and macro wont stop

Next


End Sub

【讨论】:

  • 可能是解决其他问题的好方法,但这不适用于我的用例。我的范围由不相邻的单元格(和多列)组成。除此之外,我需要的解决方案是使用For each singleCell In SomeRange.Cells 删除单元格所在的行开始下一次循环迭代之前。
【解决方案3】:

为了严格遵循您声明的自上而下删除的愿望,并允许可能在一行上多次点击,并允许不连续的范围,我建议不要使用 for循环,而是在 someRange 中的区域和行上使用 Do Loop,并在每一行上使用 For 循环(当你受到打击时终止 For )。类似的东西

Sub Demo()
    Dim rng As Range, cl As Range
    Dim someRange As Range
    Dim AreaRange As Range
    Dim DeletedRow As Boolean
    Dim areaNum As Long
    Dim i As Long

    ' Set your test range, eg
    Set someRange = [A1:F3,A8:F10,A16:F19]
    areaNum = 1
    ' Set first row to test
    Do
        ' Track discontiguous range areas
        Set AreaRange = someRange.Areas(areaNum)
        Set rng = someRange.Areas(areaNum).Rows(1)
        Do Until rng Is Nothing
            DeletedRow = False
            For Each cl In rng.Cells
                ' test condition, eg empty
                If IsEmpty(cl) Then
                    ' update rng to next row.  do this before the Delete
                    Set rng = Application.Intersect(someRange, rng.Offset(1, 0))
                    ' delete row
                    cl.EntireRow.Delete
                    DeletedRow = True
                    ' stop looping the row
                    Exit For
                End If
            Next
            ' if not already updated...
            If Not DeletedRow Then
                Set rng = Application.Intersect(someRange, rng.Offset(1, 0))
            End If
        Loop

        ' not all rows in area have been deleted, increment AreaNum
        ' AreaRange end up in a state where it's not Nothing, and isn't valid
        On Error Resume Next
            i = 0
            i = AreaRange.Count
        On Error GoTo 0
        If i > 0 Then
            areaNum = areaNum + 1
        End If
    Loop Until areaNum > someRange.Areas.Count
End Sub

请注意,这将是非常低效的,但确实符合您的既定目标

【讨论】:

    猜你喜欢
    • 2014-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多