简体   繁体   English

使用excel vba过滤出多个条件

[英]filter out multiple criteria using excel vba

I have 8 variables in column A, 1,2,3,4,5 and A, B, C.我在 A、1、2、3、4、5 和 A、B、C 列中有 8 个变量。
My aim is to filter out A, B, C and display only 1-5.我的目标是过滤掉 A、B、C 并只显示 1-5。

I can do this using the following code:我可以使用以下代码执行此操作:

My_Range.AutoFilter Field:=1, Criteria1:=Array("1", "2", "3","4","5"), _
    Operator:=xlFilterValues

But what the code does is it filters variables 1 to 5 and displays them.但是代码所做的是过滤变量 1 到 5 并显示它们。

I want to do the opposite, but yielding the same result, by filtering out A, B, C and showing variables 1 to 5我想做相反的事情,但通过过滤掉 A、B、C 并显示变量 1 到 5 来产生相同的结果

I tried this code:我试过这个代码:

My_Range.AutoFilter Field:=1, Criteria1:=Array("<>A", "<>B", "<>C"), _
    Operator:=xlFilterValues

But it did not work.但它没有用。

Why cant I use this code ?为什么我不能使用这个代码?

It gives this error:它给出了这个错误:

Run time error 1004 autofilter method of range class failed运行时错误 1004 范围类的自动过滤方法失败

How can I perform this?我该如何执行此操作?

I think (from experimenting - MSDN is unhelpful here) that there is no direct way of doing this.我认为(从实验中 - MSDN 在这里没有帮助)没有直接的方法可以做到这一点。 Setting Criteria1 to an Array is equivalent to using the tick boxes in the dropdown - as you say it will only filter a list based on items that match one of those in the array.Criteria1设置为Array等效于使用下拉列表中的复选框 - 正如您所说,它只会根据与数组中的项目匹配的项目过滤列表。

Interestingly, if you have the literal values "<>A" and "<>B" in the list and filter on these the macro recorder comes up with有趣的是,如果列表中有文字值"<>A""<>B"并在这些值上进行过滤,宏记录器会给出

Range.AutoFilter Field:=1, Criteria1:="=<>A", Operator:=xlOr, Criteria2:="=<>B"

which works.哪个有效。 But if you then have the literal value "<>C" as well and you filter for all three (using tick boxes) while recording a macro, the macro recorder replicates precisely your code which then fails with an error.但是,如果您随后也有文字值"<>C" ,并且您在录制宏时过滤了所有三个(使用勾选框),则宏录制器会精确复制您的代码,然后该代码会失败并出现错误。 I guess I'd call that a bug - there are filters you can do using the UI which you can't do with VBA.我想我会称其为错误 - 您可以使用 UI 执行过滤器,而 VBA 则无法执行这些过滤器。

Anyway, back to your problem.无论如何,回到你的问题。 It is possible to filter values not equal to some criteria, but only up to two values which doesn't work for you:可以过滤不等于某些条件的值,但最多只能过滤两个对您不起作用的值:

Range("$A$1:$A$9").AutoFilter Field:=1, Criteria1:="<>A", Criteria2:="<>B", Operator:=xlAnd

There are a couple of workarounds possible depending on the exact problem:根据确切的问题,有几种可能的解决方法:

  1. Use a "helper column" with a formula in column B and then filter on that - eg =ISNUMBER(A2) or =NOT(A2="A", A2="B", A2="C") then filter on TRUE在 B 列中使用带有公式的“辅助列”,然后对其进行过滤 - 例如=ISNUMBER(A2)=NOT(A2="A", A2="B", A2="C")然后过滤TRUE
  2. If you can't add a column, use autofilter with Criteria1:=">-65535" (or a suitable number lower than any you expect) which will filter out non-numeric values - assuming this is what you want如果您无法添加列,请使用带有Criteria1:=">-65535" (或低于您预期的合适数字)的自动过滤器,这将过滤掉非数字值 - 假设这是您想要的
  3. Write a VBA sub to hide rows (not exactly the same as an autofilter but it may suffice depending on your needs).编写一个 VBA 子程序来隐藏行(与自动过滤器不完全相同,但根据您的需要它可能就足够了)。

For example:例如:

Public Sub hideABCRows(rangeToFilter As Range)
  Dim oCurrentCell As Range
  On Error GoTo errHandler

  Application.ScreenUpdating = False
  For Each oCurrentCell In rangeToFilter.Cells
    If oCurrentCell.Value = "A" Or oCurrentCell.Value = "B" Or oCurrentCell.Value = "C" Then
      oCurrentCell.EntireRow.Hidden = True
    End If
  Next oCurrentCell

  Application.ScreenUpdating = True
  Exit Sub

errHandler:
    Application.ScreenUpdating = True
End Sub

I don't have found any solution on Internet, so I have implemented one.我在互联网上没有找到任何解决方案,所以我实现了一个。

The Autofilter code with criteria is then带有标准的自动过滤器代码然后是

iColNumber = 1
Dim aFilterValueArray() As Variant
Call ConstructFilterValueArray(aFilterValueArray, iColNumber, Array("A", "B", "C"))

ActiveSheet.range(sRange).AutoFilter Field:=iColNumber _
    , Criteria1:=aFilterValueArray _
    , Operator:=xlFilterValues

In fact, the ConstructFilterValueArray() method (not function) get all distinct values that it found in a specific column and remove all values present in last argument.事实上,ConstructFilterValueArray() 方法(不是函数)获取它在特定列中找到的所有不同值,并删除最后一个参数中存在的所有值。

The VBA code of this method is这个方法的VBA代码是

'************************************************************
'* ConstructFilterValueArray()
'************************************************************

Sub ConstructFilterValueArray(a() As Variant, iCol As Integer, aRemoveArray As Variant)

    Dim aValue As New Collection
    Call GetDistinctColumnValue(aValue, iCol)
    Call RemoveValueList(aValue, aRemoveArray)
    Call CollectionToArray(a, aValue)

End Sub

'************************************************************
'* GetDistinctColumnValue()
'************************************************************

Sub GetDistinctColumnValue(ByRef aValue As Collection, iCol As Integer)

    Dim sValue As String

    iEmptyValueCount = 0
    iLastRow = ActiveSheet.UsedRange.Rows.Count

    Dim oSheet: Set oSheet = Sheets("X")

    Sheets("Data")
        .range(Cells(1, iCol), Cells(iLastRow, iCol)) _
            .AdvancedFilter Action:=xlFilterCopy _
                          , CopyToRange:=oSheet.range("A1") _
                          , Unique:=True

    iRow = 2
    Do While True
        sValue = Trim(oSheet.Cells(iRow, 1))
        If sValue = "" Then
            If iEmptyValueCount > 0 Then
                Exit Do
            End If
            iEmptyValueCount = iEmptyValueCount + 1
        End If

        aValue.Add sValue
        iRow = iRow + 1
    Loop

End Sub

'************************************************************
'* RemoveValueList()
'************************************************************

Sub RemoveValueList(ByRef aValue As Collection, aRemoveArray As Variant)

    For i = LBound(aRemoveArray) To UBound(aRemoveArray)
        sValue = aRemoveArray(i)
        iMax = aValue.Count
        For j = iMax To 0 Step -1
            If aValue(j) = sValue Then
                aValue.Remove (j)
                Exit For
            End If
        Next j
     Next i

End Sub

'************************************************************
'* CollectionToArray()
'************************************************************

Sub CollectionToArray(a() As Variant, c As Collection)

    iSize = c.Count - 1
    ReDim a(iSize)

    For i = 0 To iSize
        a(i) = c.Item(i + 1)
    Next

End Sub

This code can certainly be improved in returning an Array of String but working with Array in VBA is not easy.这段代码当然可以在返回字符串数组方面得到改进,但在 VBA 中使用数组并不容易。

CAUTION: this code work only if you define a sheet named X because CopyToRange parameter used in AdvancedFilter() need an Excel Range !注意:此代码仅在您定义名为 X 的工作表时才有效,因为 AdvancedFilter() 中使用的 CopyToRange 参数需要 Excel 范围!

It's a shame that Microfsoft doesn't have implemented this solution in adding simply a new enum as xlNotFilterValues !遗憾的是,微软没有通过简单地添加一个新的枚举作为 xlNotFilterValues 来实现这个解决方案! ... or xlRegexMatch ! ...或 xlRegexMatch !

Alternative using VBA's Filter function使用 VBA 的 Filter 函数的替代方法

As an innovative alternative to @schlebe 's recent answer, I tried to use the Filter function integrated in VBA , which allows to filter out a given search string setting the third argument to False.作为@schlebe 最近答案的创新替代方案,我尝试使用集成在VBA 中Filter函数,它允许过滤掉给定的搜索字符串,将第三个参数设置为 False。 All "negative" search strings (eg A, B, C) are defined in an array.所有“否定”搜索字符串(例如 A、B、C)都定义在一个数组中。 I read the criteria in column A to a datafield array and basicly execute a subsequent filtering (A - C) to filter these items out.我将 A 列中的条件读取到数据字段数组中,并基本上执行后续过滤 (A - C) 以过滤掉这些项目。

Code代码

Sub FilterOut()
Dim ws  As Worksheet
Dim rng As Range, i As Integer, n As Long, v As Variant
' 1) define strings to be filtered out in array
  Dim a()                    ' declare as array
  a = Array("A", "B", "C")   ' << filter out values
' 2) define your sheetname and range (e.g. criteria in column A)
  Set ws = ThisWorkbook.Worksheets("FilterOut")
  n = ws.Range("A" & ws.Rows.Count).End(xlUp).row
  Set rng = ws.Range("A2:A" & n)
' 3) hide complete range rows temporarily
  rng.EntireRow.Hidden = True
' 4) set range to a variant 2-dim datafield array
  v = rng
' 5) code array items by appending row numbers
  For i = 1 To UBound(v): v(i, 1) = v(i, 1) & "#" & i + 1: Next i
' 6) transform to 1-dim array and FILTER OUT the first search string, e.g. "A"
  v = Filter(Application.Transpose(Application.Index(v, 0, 1)), a(0), False, False)
' 7) filter out each subsequent search string, i.e. "B" and "C"
  For i = 1 To UBound(a): v = Filter(v, a(i), False, False): Next i
' 8) get coded row numbers via split function and unhide valid rows
  For i = LBound(v) To UBound(v)
      ws.Range("A" & Split(v(i) & "#", "#")(1)).EntireRow.Hidden = False
  Next i
End Sub

An option using AutoFilter使用自动筛选的选项


Option Explicit

Public Sub FilterOutMultiple()
    Dim ws As Worksheet, filterOut As Variant, toHide As Range

    Set ws = ActiveSheet
    If Application.WorksheetFunction.CountA(ws.Cells) = 0 Then Exit Sub 'Empty sheet

    filterOut = Split("A B C D E F G")

    Application.ScreenUpdating = False
    With ws.UsedRange.Columns("A")
        If ws.FilterMode Then .AutoFilter
       .AutoFilter Field:=1, Criteria1:=filterOut, Operator:=xlFilterValues
        With .SpecialCells(xlCellTypeVisible)
            If .CountLarge > 1 Then Set toHide = .Cells 'Remember unwanted (A, B, and C)
        End With
       .AutoFilter
        If Not toHide Is Nothing Then
            toHide.Rows.Hidden = True                   'Hide unwanted (A, B, and C)
           .Cells(1).Rows.Hidden = False                'Unhide header
        End If
    End With
    Application.ScreenUpdating = True
End Sub

Here an option using a list written on some range, populating an array that will be fiiltered.这是一个使用在某个范围内写入的列表的选项,填充将被过滤的数组。 The information will be erased then the columns sorted.信息将被删除,然后对列进行排序。

Sub Filter_Out_Values()

'Automation to remove some codes from the list
Dim ws, ws1 As Worksheet
Dim myArray() As Variant
Dim x, lastrow As Long
Dim cell As Range

Set ws = Worksheets("List")
Set ws1 = Worksheets(8)
lastrow = ws.Cells(Application.Rows.Count, 1).End(xlUp).Row

'Go through the list of codes to exclude
For Each cell In ws.Range("A2:A" & lastrow)

    If cell.Offset(0, 2).Value = "X" Then 'If the Code is associated with "X"
        ReDim Preserve myArray(x) 'Initiate array
        myArray(x) = CStr(cell.Value) 'Populate the array with the code
        x = x + 1 'Increase array capacity
        ReDim Preserve myArray(x) 'Redim array
    End If

Next cell

lastrow = ws1.Cells(Application.Rows.Count, 1).End(xlUp).Row
ws1.Range("C2:C" & lastrow).AutoFilter field:=3, Criteria1:=myArray, Operator:=xlFilterValues
ws1.Range("A2:Z" & lastrow).SpecialCells(xlCellTypeVisible).ClearContents
ws1.Range("A2:Z" & lastrow).AutoFilter field:=3

'Sort columns
lastrow = ws1.Cells(Application.Rows.Count, 1).End(xlUp).Row
'Sort with 2 criteria
With ws1.Range("A1:Z" & lastrow)
    .Resize(lastrow).Sort _
    key1:=ws1.Columns("B"), order1:=xlAscending, DataOption1:=xlSortNormal, _
    key2:=ws1.Columns("D"), order1:=xlAscending, DataOption1:=xlSortNormal, _
    Header:=xlYes, MatchCase:=False, Orientation:=xlTopToBottom, SortMethod:=xlPinYin
End With

End Sub

This works for me: This is a criteria over two fields/columns (9 and 10), this filters rows with values >0 on column 9 and rows with values 4, 7, and 8 on column 10. lastrow is the number of rows on the data section.这对我有用:这是两个字段/列(9 和 10)的标准,这将过滤第 9 列值 > 0 的行和第 10 列值lastrow和 8 的行。 lastrow是行数在数据部分。

ActiveSheet.Range("$A$1:$O$" & lastrow).AutoFilter Field:=9, Criteria1:=">0", Operator:=xlAnd
ActiveSheet.Range("$A$1:$O$" & lastrow).AutoFilter Field:=10, Criteria1:=Arr("4","7","8"), Operator:=xlFilterValues

Please check this one for filtering out values in a range.请检查这个以过滤出范围内的值。 It works有用

Selection.AutoFilter field:=33, Criteria1:="<>Array(IN1R,IN2R,INDA)", Operator:=xlFilterValues Selection.AutoFilter field:=33, Criteria1:="<>Array(IN1R,IN2R,INDA)", Operator:=xlFilterValues

Actually the above code did not work.实际上上面的代码不起作用。 Hence I given a loop to hide the entire row whenever the active cell had the value that I am searching for.因此,只要活动单元格具有我正在搜索的值,我就会给出一个循环来隐藏整行。

For each cell in selection If cell.value = “IN1R” or cell.value = “INR2” or cell.value = “INDA” then对于选择中的每个单元格 如果 cell.value = “IN1R” 或 cell.value = “INR2” 或 cell.value = “INDA” 然后

  Else

  Activecell.Entirerow.Hidden = True

 End if

Next下一个

I have 8 variables in column A, 1,2,3,4,5 and A, B, C.我在A,1、2、3、4、5列和A,B,C中有8个变量。
My aim is to filter out A, B, C and display only 1-5.我的目的是过滤掉A,B,C并仅显示1-5。

I can do this using the following code:我可以使用以下代码执行此操作:

My_Range.AutoFilter Field:=1, Criteria1:=Array("1", "2", "3","4","5"), _
    Operator:=xlFilterValues

But what the code does is it filters variables 1 to 5 and displays them.但是代码的作用是过滤变量1到5并显示它们。

I want to do the opposite, but yielding the same result, by filtering out A, B, C and showing variables 1 to 5我想做相反的事情,但通过滤除A,B,C并显示变量1至5产生相同的结果

I tried this code:我尝试了这段代码:

My_Range.AutoFilter Field:=1, Criteria1:=Array("<>A", "<>B", "<>C"), _
    Operator:=xlFilterValues

But it did not work.但这没有用。

Why cant I use this code ?为什么我不能使用此代码?

It gives this error:它给出了这个错误:

Run time error 1004 autofilter method of range class failed范围类别的执行阶段错误1004自动筛选方法失败

How can I perform this?我该如何执行呢?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM