【问题标题】:Selection based on finding 2 different words in 2 columns基于在 2 列中找到 2 个不同的单词进行选择
【发布时间】:2015-08-08 16:58:32
【问题描述】:

我想使用 Excel VBA 执行以下操作:

1) 在列中查找某个 word_1;

2) 如果在步骤 (1) 中找到 word_1,则向右移动一列并寻找另一个名为 word_2 的单词。如果也找到 word_2,则删除整行。

另一方面,如果没有找到 word_2,则不必删除该行。

一般的想法是在一列中搜索多个单词,如果找到它们,还要仔细检查(为了安全起见)某些附属单词是否在第 2 列中。只有这样才能删除整行。

我做了以下小例子进行测试:

Col1 Col2

xxx xxx
xxx xxx
xxx xxx
findme  acg
xxx xxx
findme  xxx

在此示例中,我在第 1 列中搜索单词“findme”,在第 2 列中搜索相关单词“acg”。如您所见,必须删除第 4 行,因为这两个单词都出现在一行中,与例如相反第 6 行,情况并非如此。

我的最终代码:

    Sub xxx()

    Dim aCell As Range, bCell As Range, aSave As String

    Dim fndOne As String, fndTwo As String
    fndOne = "findme"
    fndTwo = "acg"

    Dim ws As Worksheet: Set ws = ActiveWorkbook.ActiveSheet

    Application.DisplayAlerts = False
    Application.ScreenUpdating = False

    With ws

        Set aCell = .Columns(1).Find(What:=fndOne, LookIn:=xlValues, _
            lookat:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
            MatchCase:=False, SearchFormat:=False)

        If Not aCell Is Nothing Then

            aSave = aCell.Address

            Do

                If LCase(.Cells(aCell.row, 2).Value) Like Chr(42) & fndTwo & Chr(42) Then

                    If bCell Is Nothing Then
                        Set bCell = .Range("A" & aCell.row)
                    Else
                        Set bCell = Union(bCell, .Range("A" & aCell.row))
                    End If

                End If

                Set aCell = .Columns(1).FindNext(After:=aCell)

            Loop Until aCell.Address = aSave

        End If

        Set aCell = Nothing
        If Not bCell Is Nothing Then bCell.EntireRow.Delete


    End With

    Application.DisplayAlerts = True
    Application.ScreenUpdating = True

End Sub

【问题讨论】:

  • 根据您的叙述,我无法确定 .Find 方法的 lookat:=xlPart 是否是故意的。您是否要匹配包含整个单词的搜索字符串或单元格的任何部分?
  • @Jeeped 是的任何部分,这是有意的。抱歉,示例没有显示
  • 我已将所有搜索条件用星号(ASCII 字符 0042)包裹起来,并将 xlWhole 更改为 xlPart 以适应通配符搜索。

标签: excel vba search match multiple-columns


【解决方案1】:

如果您使用Range.Find methodRange.FindNext method,在每次删除后删除并检查匹配的记录,您应该能够快速遍历所有可能性。

'delete rows as they are found
Sub delTwofers()
    Dim rw As Long, n As Long, cnt As Long, rng As Range
    Dim v As Long, sALLTERMs As String, vPAIRs As Variant, vTERMs As Variant

    On Error GoTo bm_SafeExit
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    Application.Calculation = xlCalculationManual

    Debug.Print Timer

    sALLTERMs = "aa;bb|cc;dd|ee;ff"

    With Worksheets("Sheet1")   'set this worksheet reference properly!
        vPAIRs = Split(LCase(sALLTERMs), Chr(124))
        For v = LBound(vPAIRs) To UBound(vPAIRs)
            vTERMs = Split(vPAIRs(v), Chr(59))
            cnt = Application.CountIfs(.Columns(1), Chr(42) & vTERMs(0) & Chr(42), .Columns(2), Chr(42) & vTERMs(1) & Chr(42))
            rw = 1
            For n = 1 To cnt
                rw = .Columns(1).Find(what:=vTERMs(0), lookat:=xlPart, _
                                      after:=.Columns(1).Cells(rw + (rw <> 1)), MatchCase:=False).Row
                Do While True
                    If LCase(.Cells(rw, 2).Value2) Like Chr(42) & vTERMs(1) & Chr(42) Then
                        .Rows(rw).Delete
                        Exit Do
                    Else
                        rw = .Columns(1).FindNext(after:=.Cells(rw, 1)).Row
                    End If
                Loop
            Next n
        Next v
    End With

    Debug.Print Timer

bm_SafeExit:
    Application.Calculation = xlCalculationAutomatic
    Application.ScreenUpdating = True
    Application.EnableEvents = True
End Sub

'collect rows with Union, delete them all at once
Sub delTwofers2()
    Dim rw As Long, n As Long, cnt As Long, rng As Range
    Dim v As Long, sALLTERMs As String, vPAIRs As Variant, vTERMs As Variant

    On Error GoTo bm_SafeExit
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    Application.Calculation = xlCalculationManual

    Debug.Print Timer

    sALLTERMs = "aa;bb|cc;dd|ee;ff"

    With Worksheets("Sheet1")   'set this worksheet reference properly!
        vPAIRs = Split(LCase(sALLTERMs), Chr(124))
        For v = LBound(vPAIRs) To UBound(vPAIRs)
            vTERMs = Split(vPAIRs(v), Chr(59))
            cnt = Application.CountIfs(.Columns(1), Chr(42) & vTERMs(0) & Chr(42), .Columns(2), Chr(42) & vTERMs(1) & Chr(42))
            rw = 1
            For n = 1 To cnt
                rw = .Columns(1).Find(what:=vTERMs(0), lookat:=xlPart, _
                                      after:=.Columns(1).Cells(rw), MatchCase:=False).Row
                Do While True
                    If LCase(.Cells(rw, 2).Value2) Like Chr(42) & vTERMs(1) & Chr(42) Then
                        If rng Is Nothing Then
                            Set rng = .Cells(rw, 1)
                        Else
                            Set rng = Union(rng, .Cells(rw, 1))
                        End If
                        Exit Do
                    Else
                        rw = .Columns(1).FindNext(after:=.Cells(rw, 1)).Row
                    End If
                Loop
            Next n
        Next v
    End With

    Debug.Print Timer  'check timer before deleting discontiguous rows
    If Not rng Is Nothing Then _
        rng.EntireRow.Delete

    Debug.Print Timer

bm_SafeExit:
    Application.Calculation = xlCalculationAutomatic
    Application.ScreenUpdating = True
    Application.EnableEvents = True
End Sub

通过首先检查确保有什么要删除,可以避免一些错误控制;您只需要找到您知道存在的双重匹配条件的条目。

附录:删除不连续行的集合非常耗时。上面的第二个例程 (delTwofers2) 比在找到行时删除行的例程慢 5%。 25,000 个值,755 个随机删除 - 第一个需要 3.60 秒;后者为 3.75 秒。

【讨论】:

  • 代码看起来很棒,但不幸的是它对我不起作用,因为我正在寻找单词的任何部分。因此,CountIfs 不能应用,If LCase(.Cells(rw, 2).Value2) = fndTwo 行也不能应用。我想知道的另一件事是:为什么您更喜欢在循环时删除行,而不是在最后将其作为一个整体进行,之前使用了相应行的union
  • @EDC - 我对通配符搜索进行了修改;将= 更改为Like,将xlWhole 更改为xlPart,并为通配符匹配添加了星号。
  • 这个过程的本质是,它只在知道那里有东西时才去寻找要删除的东西。虽然可以通过收集行并一次删除它们来调整它以获得最佳性能,但在任何性能改进之前必须有很多行。
  • 好的,谢谢您的信息。就我而言,我正在寻找很多单词,并且要删除数千行。我将尝试合并代码以创建基于union 的解决方案(可能在我的原始帖子中)。也许你以后可以看看它? :)
  • 我会再看一遍,但我觉得简单地关闭屏幕更新带来的性能提升会比将要删除的行分组产生更大的差异。
【解决方案2】:

此代码使用您的条件将过滤器应用于已用范围的前两列。然后它会删除可见的行:

Sub DeleteSelected()
Dim RangeToFilter As Excel.Range

Set RangeToFilter = ActiveSheet.UsedRange
With RangeToFilter
    .AutoFilter Field:=1, Criteria1:="find me"
    .AutoFilter Field:=2, Criteria1:="access granted"
    .SpecialCells(xlCellTypeVisible).EntireRow.Delete Shift:=xlUp
End With
End Sub

【讨论】:

  • 谢谢,看起来不错。但是,我必须使用许多单词对来做这件事,另外我正在考虑在“授予访问权限”旁边允许变量 word_2 使用多个单词。我不认为这是一个问题,它可能只是意味着添加一些循环和数组。但总的来说,我更喜欢“查找”解决方案,因为我可以看到更多代码中发生的事情。
猜你喜欢
  • 2012-06-24
  • 2012-01-06
  • 1970-01-01
  • 2015-06-27
  • 1970-01-01
  • 1970-01-01
  • 2021-11-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多