【问题标题】:For Loop not fully cycling in excel VBA [duplicate]For Loop在excel VBA中没有完全循环[重复]
【发布时间】:2020-05-15 10:22:07
【问题描述】:

我有一个宏,我在其中连续搜索文本,如果一列没有我指定的文本,则将其删除。这是我的代码:

Private Sub Test()

Dim lColumn As Long
    lColumn = ActiveSheet.Cells(2, Columns.Count).End(xlToLeft).Column


Dim i As Long
Dim myCell As Range
Dim myRange As Range
Set myRange = Worksheets("2019").Range(Cells(2, 1), Cells(2, lColumn))

For Each myCell In myRange
  If Not myCell Like "*($'000s)*" And Not myCell Like "*Stmt Entry*" And Not myCell Like "*TCF*" And_ 
  Not myCell Like "*Subtotal*" And Not myCell Like "*Hold*" Then
    myCell.EntireColumn.Select
    Selection.Delete
  End If

Next

End Sub

我的问题是,当我执行宏时,它只会删除一些列,但不会删除范围末尾的列。如果我再次运行宏,它将成功删除我要求它删除的所有列。

如果我将宏切换为 - 比方说 - 使单元格变为粗体而不是删除它们,它每次都能完美运行。

我错过了什么?

非常感谢!

【问题讨论】:

  • 如果是循环删除,应该从右向左循环。
  • 我该怎么做?
  • 您在迭代集合时正在修改它。这通常意味着意外的、奇怪的行为。考虑 Union-ing 列而不是 Select-ing 列,然后运行联合 Range 对象的 .Delete 方法一次,循环之后 而不是在每次迭代时删除Selection。向后循环以保持低效地修改您正在迭代的集合只是......向后循环。
  • @BigBen 我也是。行打了几十遍,列的歌舞一模一样。 here's one
  • 建议不要使用ActiveSheet获取最后一列;使用您真正感兴趣的工作表。无法保证 ActiveSheet 会是您所期望的。

标签: excel vba


【解决方案1】:

尽管每个人都在这个和链接的帖子中说“只是向后循环”,但这不是你想要做的。

它会起作用,然后你的下一个问题将是“我怎样才能加快这个循环”。

真正的解决方案是停止你正在做的事情,并以不同的方式做事。 在迭代时修改集合绝不是一个好主意。

从一个可以将两个范围合二为一的辅助函数开始:

Private Function CombineRanges(ByVal source As Range, ByVal toCombine As Range) As Range
    If source Is Nothing Then
        'note: returns Nothing if toCombine is Nothing
        Set CombineRanges = toCombine
    Else
        Set CombineRanges = Union(source, toCombine)
    End If
End Function

然后声明一个toDelete 范围并在迭代时使用这个CombineRanges 函数构建(“选择”)一个Range - 请注意,这个循环不会修改任何单元格任何地方:

Dim sheet As Worksheet
' todo: use sheet's codename instead if '2019' is in ThisWorkbook
Set sheet = ActiveWorkbook.Worksheets("2019")

Dim source As Range
' note: qualified .Cells member calls refer to same sheet as .Range call
Set source = sheet.Range(sheet.Cells(2, 1), sheet.Cells(2, lColumn))

Dim toDelete As Range
Dim cell As Range
For Each cell In source
    'note: needed because comparing cell.Value with anything will throw error 13 "type mismatch" if cell contains a worksheet error value.
    'alternatively, use cell.Text.
    If Not IsError(cell.Value) Then
        If Not cell.Value Like "*($'000s)*" _
            And Not cell.Value Like "*Stmt Entry*" _
            And Not cell.Value Like "*TCF*" _
            And Not cell.Value Like "*Subtotal*" _
            And Not cell.Value Like "*Hold*" _
        Then
            Set toDelete = CombineRanges(cell, toDelete)
        End If
    End If
Next

最后一步是删除toDelete 范围内的.EntireColumn...如果此时不是Nothing

If Not toDelete Is Nothing Then toDelete.EntireColumn.Delete

【讨论】:

  • 难以置信!这个答案怎么会有 4 个赞,而我的结果几乎相同的答案是 0,即使我的答案是两分钟前的惊人结果。这就是 BigBen 所做的一切——将我的代码称为 HACK。因为我无事可做,所以我要给这个答案添加第五个赞?,因为我正在努力赢得 stackoverflow,“通过投票给竞争性答案,你是一个很棒的运动奖杯 i>" 更严重的是……If not is nothing Then xx.Delete 的最后防守很好。我没有那个(这可能就是为什么我只有不到 10% 的 SO 街头信誉....)。 ?
  • 好吧,要么我妈妈又回到了 StackOverflow 上,要么一定有人读到了我的抱怨并给了我一个赞。 谢谢! 对任何有信誉的人的严重问题:请参阅我创建最后/行列方法的方法,以避免在每个循环上使用 if 语句。关于这是否更好/更差/太微不足道而值得担心的想法?谢谢。
  • @PGSystemTester 将killrng 初始分配到工作表的最后一列,以确保killrng 不是Nothing,这是不需要发生空值的无关工作查看。虽然该列中确实不太可能有任何有价值的东西,但宏不应影响没有理由受到影响的单元格:使用该“hack”意味着将假设放入代码中,并且任何可以删除的假设 从代码中应该从代码中删除。
  • 谢谢。我很感激反馈。我可以忍受最后一行出现问题的可能性,但是我认为我的速度有所提高。我进行了一些测试....根本没有显着改善。所以我同意。感谢您的反馈!
猜你喜欢
  • 2013-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-03
  • 2016-02-26
  • 1970-01-01
  • 2013-10-04
  • 2017-08-08
相关资源
最近更新 更多