отфильтровать несколько критериев с помощью excel vba

у меня есть 8 переменных в столбце A, 1,2,3,4,5 и A, B, C.

моя цель-отфильтровать A, B, C и отобразить только 1-5.

Я могу сделать это, используя следующий код:

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

но что делает код, это фильтрует переменные от 1 до 5 и отображает их.

Я буду делать противоположное, но давать тот же результат, отфильтровывая A, B, C и показывая переменные от 1 до 5

Я пробовал этот код:

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

но это не работать.

почему я не могу использовать этот код ?

это дает эту ошибку:

Ошибка времени выполнения 1004 метод автофильтра класса диапазона не удалось

как я могу выполнить это?

5 ответов


я думаю (из экспериментов - MSDN здесь бесполезно), что нет прямого способа сделать это. Настройка Criteria1 до Array эквивалентно использованию флажков в раскрывающемся списке - как вы говорите, он будет фильтровать только список на основе элементов, соответствующих одному из них в массиве.

интересно, если у вас есть буквальные значения "<>A" и "<>B" в списке и фильтре на этих макрос рекордер придумывает

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

, которая работает. Но если вы имеют буквальное значение "<>C" а также, и вы фильтруете для всех трех (используя галочки) во время записи макроса, макрос рекордер реплицирует именно ваш код, который затем терпит неудачу с ошибкой. Думаю, я бы назвал это ошибкой - есть фильтры, которые вы можете сделать, используя пользовательский интерфейс, который вы не можете сделать с VBA.

в любом случае, вернемся к вашей проблеме. Можно фильтровать значения, не равные некоторым критериям, но только до двух значений, которые не работают для вас:

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

там возможны ли несколько обходных путей в зависимости от точной проблемы:

  1. используйте "вспомогательный столбец" с формулой в столбце B, а затем отфильтруйте это - например =ISNUMBER(A2) или фильтр TRUE
  2. если вы не можете добавить столбец, используйте автофильтр с Criteria1:=">-65535" (или подходящее число ниже, чем вы ожидаете), которое будет отфильтровывать нечисловые значения-предполагая, что это то, что вы хотите
  3. напишите суб VBA, чтобы скрыть строки (не совсем то же самое, что автофильтр, но это может быть достаточно, в зависимости от ваших потребностей).

например:

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

Я не нашел никакого решения в интернете, поэтому я реализовал его.

код Автофильтра с критериями тогда

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

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

фактически, метод ConstructFilterValueArray () (не функция) получает все различные значения, найденные в определенном столбце, и удаляет все значения, присутствующие в последнем аргументе.

код 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

этот код, безусловно, может быть улучшен при возврате массива строк, но работе с Массив в VBA непростой.

Внимание: этот код работает, только если вы определяете лист с именем X, потому что параметр CopyToRange, используемый в AdvancedFilter () нужен диапазон Excel !

жаль, что Microfsoft не реализовал это решение, добавив просто новое перечисление как xlNotFilterValues ! ... или xlRegexMatch !


альтернатива с помощью функции фильтра VBA

в качестве инновационной альтернативы недавнему ответу @schlebe я попытался использовать Filter функция интегрирована в VBA, позволяющего из данную строку поиска третий аргумент в false. Все!--11-->"негативный" строки поиска (например, A, B, C) определяются в массиве. Я читаю критерии в столбце A в массив полей данных и в основном выполняю последующее фильтрация (A-C) для фильтрации этих элементов.

код

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

опция с использованием Автофильтра


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

заменить оператор:=xlOr на Оператор:=xlAnd между вашими критериями. Смотрите ниже исправленный скрипт

"мой_диапазон".Поле автофильтра:=1, Criteria1:="A", оператор:=xlAnd, Criteria2:="B", оператор:=xlAnd, Criteria3:="C"