4

After trying in vain to set more than 2 criteria in a normal AutoFilter fashion via VBA, I have come to learn that it must be done via advanced filter.

offending example:

Worksheets(1).Range("A1").AutoFilter Field:=ColNum, Criteria1:="A*", Operator:=xlOr, Criteria2:="B*", Operator:=xlOr, Criteria3:="C*"

I am hoping to pass the criteria through to a function (rather than a macro) from a PowerShell script. I have it all working fine and as expected for 1 criteria, but now I'd like 3.

I suppose I could instead write a macro to insert a new sheet, write in the criteria then filter on that new range but I'd rather check the preferred way first.

Chris
  • 304
  • 7
  • 21
  • Can't be anything but a range I'm afraid. – Rory Dec 30 '15 at 15:36
  • 1
    Create a variant array of wildcard matches and then use the array of full values with the standard [AutoFilter method](https://msdn.microsoft.com/en-us/library/office/aa221844%28v=office.11%29.aspx). I've done this with a dictionary object to use its unique index property. –  Dec 30 '15 at 15:37
  • Won't this become a custom filter and fail due to >2? – Chris Dec 30 '15 at 15:43
  • 1
    @Chris - see my generic solution below. –  Dec 30 '15 at 16:08

1 Answers1

6

To filter on multiple wildcards, create a variant array of wildcard matches and then use the array of full values with the standard AutoFilter method. You can minimize the array by putting a dictionary object to use with its unique index property.

Consider the following sample data.

      autofilter_multi_wilcard

Run this code.

Sub multiWildcards()
    Dim v As Long, vVALs As Variant, dVALs As Object
    Dim colNum As Long

    Set dVALs = CreateObject("Scripting.Dictionary")
    dVALs.comparemode = vbTextCompare
    colNum = 2 'column B

    With Worksheets(1)
        If .AutoFilterMode Then .AutoFilterMode = False
        With .Cells(1, 1).CurrentRegion
            vVALs = .Columns(colNum).Cells.Value2
            For v = LBound(vVALs, 1) To UBound(vVALs, 1)
                If Not dVALs.exists(vVALs(v, 1)) Then
                    Select Case UCase(Left(vVALs(v, 1), 1))
                        Case "A", "B", "C"
                            dVALs.Add Key:=vVALs(v, 1), Item:=vVALs(v, 1)
                        Case Else
                            'do nothing
                    End Select
                End If
            Next v

            If CBool(dVALs.Count) Then
                'populated the dictionary; now use the keys
                .AutoFilter Field:=colNum, Criteria1:=dVALs.keys, Operator:=xlFilterValues
            Else
                Debug.Print "Nothing to filter on; dictionary is empty"
            End If

            '.CurrentRegion is now filtered on A*, B*, C* in column B
            'do something with it
        End With
    End With

    dVALs.RemoveAll: Set dVALs = Nothing
End Sub

Results should be:

      autofilter_multi_wilcard_results

These results can be duplicated with many other wildcard scenarios. The Select Case statement is ideal as it supports the Like keyword for building your collection of matches. By starting with a value dump into a regular variant array, cycling through even large rows of data can be done quickly.

Community
  • 1
  • 1
  • Interesting, will give it a go. Thanks very much. Next step is passing the array of values to VBA from PowerShell! – Chris Dec 30 '15 at 16:12
  • Just tried to run this and get type mismatch on .AutoFilter Field:=colNum, Criteria1:=dVALs.keys, Operator:=xlFilterValues – Chris Dec 30 '15 at 16:18
  • This may have something to do with your version of Excel and its SP. Try `Criteria1:=(dVALs.keys)`. I honestly did not make the image up. –  Dec 30 '15 at 16:19
  • I wouldn't dare suggest you did! It's likely me making some mistake. I've added the brackets but get the same error. I'm using Excel 2013 if it helps – Chris Dec 30 '15 at 16:23
  • Could you try [this sample workbook](https://dl.dropboxusercontent.com/u/100009401/AutoFilter_Multi_Wildcards_Sample.xlsb)? It is a small XLSB. –  Dec 30 '15 at 16:25
  • If the dictionary is empty, you will receive that error. See my error control addition above. –  Dec 30 '15 at 16:56
  • Apologies for the slow reply, thank you very much for the further help. Will give it a test this morning. – Chris Jan 04 '16 at 08:53
  • I now get no error but nothing happens, I've tried updating the cases to my actual values to be filtered on, including and not including wildcard. I remember you mentioning the Like keyword but don't see it used anywhere, is this intended? I'm unable to use your test spreadsheet as dropbox is not available on this network. I run all my macros from a personal XLSB, is this sufficient? The autofilter is removed so I know the macro is being run. – Chris Jan 04 '16 at 09:06
  • I can confirm the issue now is the dictionary is empty, wasn't sure where to see the debug output so I added a MsgBox and it appears. – Chris Jan 04 '16 at 09:32
  • Ignore the above - got it working. Absolutely a case of user not being clear of requirements! Once I properly went through your code I realized it was selecting on 1 letter of the value (from the A,B,C example). My actual scenario needs to filter on the first 3, so I updated the "Select Case" line and it works perfectly. Thank you so much for your time and effort. – Chris Jan 04 '16 at 10:02
  • Glad you got that working. It's a powerful tool in just a handful of code lines. –  Jan 04 '16 at 10:45
  • Absolutely, I've put in a "sub" select to try on lengths of 2 as well which works! I want to extend it now to take an array of values, but I'll give that a go and create a separate question in the future if I get stuck. Thanks again – Chris Jan 04 '16 at 11:03
  • 1
    btw, `Debug.Print` reports to the VBE's **Immediate** window. Tap Ctrl+G to reveal it (alternately View ► Immediate Window). –  Jan 04 '16 at 11:07
  • is there a way to specifically exclude a wildcard? For example I want to include "A*" and "B*", but exclude "BB*"? – Chris Jan 05 '16 at 13:21
  • After meeting the B requirement, you could easily add a line like `If Not Left(vVALs(v, 1), 2) = "BB" Then dVALs.Add Key:=...`. If there were many of these, it might make sense to assign `vVALs(v, 1)` to a string variable just above the `Select Case` ands use the string var in all subsequent conditions. –  Jan 05 '16 at 13:44
  • Hmm ok, I need to make it a bit more flexible. This has so many comments now and I've altered the macro so it might be better to make a new question. – Chris Jan 05 '16 at 14:06
  • Drop one more note here when you do and I'll try to have a look. –  Jan 05 '16 at 14:14
  • New question here http://stackoverflow.com/questions/34614417/can-autofilter-take-both-inclusive-and-non-inclusive-wildcards-from-dictionary-k – Chris Jan 05 '16 at 14:46