отфильтровать несколько критериев с помощью 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
там возможны ли несколько обходных путей в зависимости от точной проблемы:
- используйте "вспомогательный столбец" с формулой в столбце B, а затем отфильтруйте это - например
=ISNUMBER(A2)
или фильтрTRUE
- если вы не можете добавить столбец, используйте автофильтр с
Criteria1:=">-65535"
(или подходящее число ниже, чем вы ожидаете), которое будет отфильтровывать нечисловые значения-предполагая, что это то, что вы хотите - напишите суб 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"