Apply exclusionary criteria for multiple criteria - excel

I am trying to have some VBA code that will apply an advanced/autofilter to exclude three variables in particular. For instance, `In this entire data set, hide entries for which Column A has values X, Y, or Z.
This code works for inclusions as far as I can tell
Sheets(sheetName).Range("$A:$" & finalCol).AutoFilter Field:=fieldIndex, criteria1:=Array("=" & crit1, "=" & crit2, "=" & crit3), Operator:=xlFilterValues
But this does not work for exclusions:
Sheets(sheetName).Range("$A:$" & finalCol).AutoFilter Field:=fieldIndex, criteria1:=Array("<>" & crit1, "<>" & crit2, "<>" & crit3), Operator:=xlFilterValues
Data validation code
Dim Arr() As Variant
Arr = Range(Cells(10, 2).Validation.Formula1)
For R = 1 To UBound(Arr, 1)
For C = 1 To UBound(Arr, 2)
Debug.Print Arr(R, C)
Next C
Next R
Is there a way to make this array "one dimensional" like in the answer?

Let's say your worksheet looks like this
LOGIC:
What we are doing is creating the array of values that we want and then passing that array to the autofilter.
CODE:
Try this code
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim Ar() As String, itm
Dim Lrow As Long, n As Long, i As Long
Dim rng As Range
Dim Col As New Collection
Dim TempAr
Dim ExclusionList As String
Dim doNotAdd As Boolean
'~~> Exclusion List
ExclusionList = "X,Y,Z"
TempAr = Split(ExclusionList, ",")
'~~> Change this to the relevant worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
.AutoFilterMode = False
Lrow = .Range("A" & .Rows.Count).End(xlUp).Row
Set rng = .Range("A1:A" & Lrow)
'~~> Get unique collection of items
For i = 2 To Lrow
On Error Resume Next
Col.Add .Range("A" & i).Value, CStr(.Range("A" & i).Value)
On Error GoTo 0
Next i
'~~> Create an array which doesn't have the items that we don't need
For Each itm In Col
For i = LBound(TempAr) To UBound(TempAr)
If itm = TempAr(i) Then
doNotAdd = True
Exit For
End If
Next
If doNotAdd = False Then
ReDim Preserve Ar(n)
Ar(n) = itm
n = n + 1
End If
doNotAdd = False
Next
'~~> Autofilter
rng.AutoFilter Field:=1, Criteria1:=Ar(), Operator:=xlFilterValues
End With
End Sub
OUTPUT:

Related

Remove rows containing keyword and indented records below it

Attached is a small sample of my data, row 7, 8, 12 and 13 are parent headings and rows 9-11 are child headings of row 8 because it is indented under it. When I run Range().IndentLevel, it returns 2 for rows 7, 8, 12, 13 and 3 for rows 9-11. These are the only two IndentLevels in the column
I am trying to remove all the rows with the keyword "Pursuit Adjustment" which I was able to do with the following:
Dim ws As Worksheet
Dim strSearch As String
Dim lRow As Long
strSearch = "Pursuit Adjustment"
Set ws = Sheets("PFSR All (formatted)")
With ws
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
'~~> Remove any filters
.AutoFilterMode = False
'~~> Filter, offset(to exclude headers) and delete visible rows
With .Range("A1:A" & lRow)
.AutoFilter Field:=1, Criteria1:="=*" & strSearch & "*"
.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
'~~> Remove any filters
.AutoFilterMode = False
End With
However I am having difficulties figuring out two things:
How do I also remove all child headings of the associated parent heading in my strSearch (rows 9-11)?
In my strSearch, I have only specified "Pursuit Adjustment", how can I add multiple search conditions to it?
You could try this code:
Dim ws As Worksheet
Dim i As Long, j As Long, lRow As Long
Dim strSearch As Variant
strSearch = Array("Pursuit Adjustment", "str2", "str3") 'Put here all the strings you want to search and delete
Set ws = Sheets("PFSR All (formatted)")
With ws
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
.AutoFilterMode = False
For i = LBound(strSearch) To UBound(strSearch)
For j = 1 To lRow
If InStr(.Range("A" & j), strSearch(i)) Then
.Rows(j).EntireRow.Delete
Do While .Range("A" & j).IndentLevel > 2
.Rows(j).EntireRow.Delete
Loop
j = j - 1
End If
Next j
Next i
End With
Please, try the next code, too. It will be faster, placing the necessary ranges in a Union range and delete them at once, at the end. The actual code only selects the rows to be deleted. If it returns what you need, you can replace Select with Delete on the last code line:
Sub teleteSpecificRowsAndIndentedBelow()
Dim ws As Worksheet, strSearch As String, lRow As Long, arrA, arrSearch, El
Dim i As Long, j As Long, rngDel As Range, boolFound As Boolean
arrSearch = Split("Pursuit Adjustment,second string,third string,etc", ",") 'no space after comma!!!
Set ws = ActiveSheet ' Sheets("PFSR All (formatted)")
lRow = ws.Range("A" & ws.rows.count).End(xlUp).row
arrA = ws.Range("A1:A" & lRow).Value 'put it in an array to make iteration faster
For i = 1 To UBound(arrA)
For Each El In arrSearch
If InStr(arrA(i, 1), El) > 0 Then boolFound = True: Exit For
Next
If boolFound Then
If rngDel Is Nothing Then
Set rngDel = ws.Range("A" & i)
Else
Set rngDel = Union(rngDel, ws.Range("A" & i))
End If
'start searching for indented following rows:
For j = 1 To lRow
If ws.Range("A" & i + j).IndentLevel < 2 Then Exit For
Set rngDel = Union(rngDel, ws.Range("A" & i + j))
Next j
i = i + j - 1: boolFound = False
End If
Next i
If Not rngDel Is Nothing Then rngDel.EntireRow.Select 'if the output is correct, please replace Select with Delete
End Sub

Insert Row when 2 conditions are met

I have created below code which works like IF Col"B" any cell <> "" And Col"L" any cell = "Leop" then add row below to the active cell.
I mean I'm trying to achieve is to insert single row after certain row which contain in column B any value, and if column L in same row contains value = "Leop". Then add the row after that certain row.
But an error is appear. Compile Error: Invalid use of property on xlDown
Your help will be appreciated to fix it.
From this:
to this:
Sub firstcondition()
Dim ws As Worksheet
Dim LRow As Long
Dim rng As Range
Dim rng2 As Range
Dim i As Long
Dim p As Long
Dim dat As Variant
Dim datt As Variant
Dim IRow As Long
Set ws = Thisworkbooks.Sheets("Sheet2")
With ws
LRow = .Range("A" & .Rows.Count).End(xlUp).Row
Set rng = .Range("B2:B" & LRow)
Set rng2 = .Range("L2:L" & LRow)
dat = rng
datt = rng2
IRow = Selection.Row
For i = LBound(dat, 1) To UBound(dat, 1)
For p = LBound(datt, 1) To UBound(datt, 1)
If dat(i, 1) <> "" And datt(p, 1) = "Leop" Then
Rows(IRow + 1).Select
Selection.Insert Shift: xlDown
End If
End Sub
It will be like in formula:
IF(AND(B2<>"",L2="Leop"),"InsertRowBelow to Row 2 If condition is met","")
and will drag it down to the lastRow.
Thisworkbooks.Sheets("Sheet2") should be Thisworkbook.Sheets("Sheet2") and missing = in Selection.Insert Shift:= xlDown
Inserting or deleting rows will change the last row number so start at the bottom and work upwards.
Option Explicit
Sub firstcondition()
Dim ws As Worksheet, LRow As Long, r As Long
Dim n As Long
Set ws = ThisWorkbook.Sheets("Sheet2")
With ws
LRow = .Range("B" & .Rows.Count).End(xlUp).Row
For r = LRow To 2 Step -1
If .Cells(r, "B") <> "" And .Cells(r, "L") = "Leop" Then
.Rows(r + 1).Insert shift:=xlDown
n = n + 1
End If
Next
End With
MsgBox n & " rows inserted", vbInformation
End Sub
Try this with autofilter, you dont have to loop through each row. So it will work faster for larger data.
Option Explicit
Sub firstcondition()
Dim ws As Worksheet
Dim LRow As Long, cl As Range
Set ws = ThisWorkbook.Sheets("Sheet2")
LRow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
ws.Range("L1:L" & LRow).AutoFilter 1, "Leop"
For Each cl In ws.Range("_FilterDatabase").SpecialCells(12).Cells
If ws.Range("B" & cl.Row) <> "" Then
cl.Offset(1).EntireRow.Insert Shift:=xlDown
End If
Next
ws.AutoFilterMode = False
End Sub

Fetch values in Target sheet from Source sheet based on the unique ID

There are two sheets - Source and Target. There is a unique ID in both sheets in Column-A.
In TargetSheet based on the uniqueID (Column-A), I want to fetch values from SourceSheet(Column-B) to TargetSheet(Column-B).
There are unique IDs in TargetSheet(Column-A) which are not in SourceSheet(Column-A), hence they should be left blank.
Sub Recon()
Dim lrow, i, j As Long
lrow = Range("A1048576").End(xlUp).Row
TargetSheet.Activate
Range("A1").Select
j = 1
For i = 3 To lrow
Do
j = j + 1
If Range("A" & i) = SourceSheet.Range("A" & j) Then
Cells(i, 2) = SourceSheet.Range("B" & j).Value
End If
Loop Until Range("A" & i) = SourceSheet.Range("A" & j)
Next i
End Sub
You can do it combining VLOOKUP trapped into an IFERROR to handle missing ids. VLOOKUP will look a value in a column and if found, will return a value in same row but different custom column. If there is no coincidence, it will raise an error, so we combine it with IFERROR to transform that error into blank value, which means that id is not found.
VLOOKUP function
IFERROR function
My fake data is just 2 sheets like yours:
The code I've used:
Sub test()
Dim WKsource As Worksheet
Dim WKTarget As Worksheet
Dim UF As Long
Set WKsource = ThisWorkbook.Worksheets("SourceSheet")
Set WKTarget = ThisWorkbook.Worksheets("TargetSheet")
With WKTarget
UF = .Range("A" & .Rows.Count).End(xlUp).Row
.Range("B2:B" & UF).FormulaR1C1 = "=IFERROR(VLOOKUP(RC[-1]," & WKsource.Name & "!C1:C2,2,FALSE),"""")" 'get values
.Range("B2:B" & UF) = .Range("B2:B" & UF).Value 'paste formula results as values
End With
Set WKTarget = Nothing
Set WKsource = Nothing
End Sub
My final output:
Try this:
`Sub Test()
Dim Source As Range
Set Source = ThisWorkbook.Worksheets("Source").Range("A2:A5")
Dim Target As Range
Set Target = ThisWorkbook.Worksheets("Target").Range("A2:A6")
Dim TargetCell As Range
Dim FoundCell As Range
For Each TargetCell In Target
Set FoundCell = Source.Find(TargetCell.Value, _
Source.Cells(1, 1), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchDirection:=xlNext)
If Not FoundCell Is Nothing Then
TargetCell.Offset(, 1) = FoundCell.Offset(, 1)
End If
Next TargetCell
End Sub`

How to apply multiple criteria to .Find?

I adapted code I found online.
It finds the string "car" in column A and returns the rows as an array
It assigns a variable to the length of the array (how many matches it found)
It assigns a variable to generate a random number between 0 and the length of the array
It then prints a random matching row's value into K3
Dim myArray() As Variant
Dim x As Long, y As Long
Dim msg As String
With ActiveSheet.Range("A1:A" & ActiveSheet.Range("A" & Rows.Count).End(xlUp).Row)
Set c = .find("Car", LookIn:=xlValues)
If Not c Is Nothing Then
firstAddress = c.Address
Do
ReDim Preserve myArray(y)
myArray(y) = c.Row
y = y + 1
Set c = .findNext(c)
If c Is Nothing Then
GoTo DoneFinding
End If
Loop While c.Address <> firstAddress
End If
DoneFinding:
End With
For x = LBound(myArray) To UBound(myArray)
msg = msg & myArray(x) & " "
Next x
ArrayLen = UBound(myArray) - LBound(myArray)
random_index = WorksheetFunction.RandBetween(0, ArrayLen)
MsgBox myArray(random_index)
Dim test As String
test = "B" & myArray(random_index)
Range("K3").Value = Range(test)
Example
I'm struggling with adapting the find code to allow for multiple criteria. So in my example, it finds "Car". What if I want to find matches that had "Car" in column A and "Red" in column D?
I tried
With ActiveSheet.Range("A1:A" & "D1:D" & ActiveSheet.Range("A" & Rows.Count).End(xlUp).Row & ActiveSheet.Range("D" & Rows.Count).End(xlUp).Row)
Set c = .find("Car", "Red", LookIn:=xlValues)
I get type mismatch on the Set line.
In case it is confusing, it currently looks for a string e.g. "Car" but I will eventually link this to the variable which will be assigned to a data validation list. So if the user chooses "car" from a drop down list, this is what it will search for.
Maybe Advancde Filter is something that fit your needs:
Example Code
Option Explicit
Public Sub FilterData()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("YourSheetName")
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim CriteriaRange As Range
Set CriteriaRange = ws.Range("A1", "E2")
Dim DataRange As Range
Set DataRange = ws.Range("A4", "E" & LastRow)
DataRange.AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:=CriteriaRange, Unique:=False
End Sub
Public Sub ShowAll()
On Error Resume Next
ActiveSheet.ShowAllData
On Error GoTo 0
End Sub
Edit according comment:
You can use the advanced filter and then loop through the filter results:
Option Explicit
Public CurrentRow As Long
Public Sub FilterData()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("YourSheetName")
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim CriteriaRange As Range
Set CriteriaRange = ws.Range("A1", "E2")
Dim DataRange As Range
Set DataRange = ws.Range("A4", "E" & LastRow)
DataRange.AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:=CriteriaRange, Unique:=False
End Sub
Public Sub ShowAll()
On Error Resume Next
ActiveSheet.ShowAllData
CurrentRow = 1
On Error GoTo 0
End Sub
Public Sub GetNextResult()
FilterData
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("YourSheetName")
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim DataRange As Range
Set DataRange = ws.Range("A4", "E" & LastRow)
Dim FilteredData As Range
Set FilteredData = DataRange.Resize(ColumnSize:=1).SpecialCells(xlCellTypeVisible)
If CurrentRow + 1 > FilteredData.Cells.Count Then
CurrentRow = 1
End If
CurrentRow = CurrentRow + 1
Dim i As Long
Dim Cell As Variant
For Each Cell In FilteredData
i = i + 1
If i = CurrentRow Then
Cell.EntireRow.Select
'or
'MsgBox Cell.Value & vbCrLf & Cell.Offset(0, 1) & vbCrLf & Cell.Offset(0, 2) & vbCrLf & Cell.Offset(0, 3) & vbCrLf & Cell.Offset(0, 4)
End If
Next Cell
End Sub

Error in For Each loop using AutoFilter runtime (error 13)

This is what I am trying to do
Find unique values in column D
Loop over those values by creating a filter with each
with the remaining rows after filtering, I do the same with columns E and F.
Finally, I just need to copy the remaining values in column K and past them in a different sheet.
In one of the loops the code gives me an error (see line below). I have tried to solve it in different ways and looked for an answer online, but I have not been able to find why is that happening. I got "run-time error '13' Type mismatch"
I highly appreciate any ideas. Thanks!!
Sub UniqueVals_f()
'' Variables
Dim i As Variant ' loop counter
Dim a As Variant ' loop counter
Dim R As Long
Dim W As Long
Dim Z As Long
Dim gr As Variant ' group values
Dim ca As Variant ' category value
Dim cl As Variant ' class value
Dim CategArray() As Variant
Dim GroupArray() As Variant
Dim ClassArray() As Variant
Dim My_Range As Range
Dim DestSh As Worksheet ' Destination sheet
Dim LastCol As Long
Dim rng As Range
Dim r1 As Range
Dim r2 As Range
Dim r3 As Range
' select range
Set My_Range = Worksheets("ICP").Range("D1", Range("F" & Rows.Count).End(xlUp))
My_Range.Parent.Select
My_Range.Parent.AutoFilterMode = False 'Remove the AutoFilter
' Destination sheet
Set DestSh = Sheets("items")
ca = Application.Transpose(Range("D2", Range("D" & Rows.Count).End(xlUp))) ' extract Categories
With CreateObject("Scripting.Dictionary") 'Categories array
For Each i In ca ' <-- This one works fine
.Item(i) = i
Next
CategArray = Application.Transpose(.Keys) ' getting unique values
End With
'' loop over categories
For R = 1 To UBound(CategArray, 1)
My_Range.AutoFilter Field:=1, Criteria1:="=" & CategArray(R, 1) ' First Filter
gr = Application.Transpose(Range("E2", Range("E" & Rows.Count).End(xlUp))) ' extract Groups
With CreateObject("Scripting.Dictionary")
For Each i In gr ' <-- This one works fine too
.Item(i) = i
Next
GroupArray = Application.Transpose(.Keys) ' getting unique values
End With
'' Loop over Groups
For W = 1 To UBound(GroupArray, 1)
My_Range.AutoFilter Field:=2, Criteria1:="=" & GroupArray(W, 1) ' Second Filter
lr3 = Cells(Rows.Count, 6).End(xlUp).Row '' Extract Classes
cl = Application.Transpose(Range("F2:F" & lr3))
' cl = Range("F2:F" & lr3) ' Alternative way 1
' cl = Range("F2:F" & lr3).Value2 ' Alternative way 2
With CreateObject("Scripting.Dictionary")
For Each i In cl '' <-- THE ERROR IS HERE!!!
'For i = LBound(cl, 1) To UBound(cl, 1) ' Alternative that has the same error
.Item(i) = i
Next
'Next i
ClassArray = Application.Transpose(.Keys)
End With
'' Loop over classes
For Z = 1 To UBound(ClassArray, 1)
' filter classes
My_Range.AutoFilter Field:=3, Criteria1:="=" & ClassArray(Z, 1) ' Third Filter
'' Copy items
Set rng = DestSh.Rows("2:2")
LastCol = Last(2, rng)
Range("K2", Range("K" & Rows.Count).End(xlUp)).SpecialCells(xlCellTypeVisible).Copy _
Destination:=DestSh.Cells(2, LastCol + 1)
My_Range.Parent.AutoFilterMode = False 'Remove the AutoFilter
Next Z
Next W
Next R
End Sub
Best,
Pablo
Following suggestions by A.S.H, I improved the code in the following way:
Sub UniqueVals()
Dim a As Variant ' loop counter
Dim b As Variant ' loop counter
Dim c As Variant ' loop counter
Dim Ccolumn As Long
Dim My_Range As Range
Dim MainSh As Worksheet ' Main sheet
Dim DestSh As Worksheet ' Destination sheet
Dim AuxSh As Worksheet ' Aux sheet
Dim LastCol As Long
Dim CategRg As Excel.Range
Dim GroupRg As Excel.Range
Dim ClassRg As Excel.Range
Application.ScreenUpdating = False
' Destination sheet
Set MainSh = Sheets("ICP")
Set DestSh = Sheets("items")
Set AuxSh = Sheets("Aux")
' select range
Set My_Range = MainSh.Range("D1", Range("F" & Rows.Count).End(xlUp))
My_Range.Parent.Select
My_Range.Parent.AutoFilterMode = False 'Remove the AutoFilter
Ccolumn = 1
'' extract Categories
Range("D2", Range("D1").End(xlDown)).Copy
AuxSh.Range("A1").PasteSpecial Paste:=xlPasteValues
AuxSh.Range("A1").CurrentRegion.RemoveDuplicates Columns:=1, Header:=xlNo
Set CategRg = AuxSh.Range("A1", AuxSh.Range("A" & Rows.Count).End(xlUp))
For Each a In CategRg.SpecialCells(xlCellTypeVisible)
My_Range.AutoFilter Field:=1, Criteria1:="=" & a.Value
MainSh.Range("E2", MainSh.Range("E1").End(xlDown)).Copy
AuxSh.Range("B1").PasteSpecial Paste:=xlPasteValues
AuxSh.Range("B:B").RemoveDuplicates Columns:=1, Header:=xlNo
Set GroupRg = AuxSh.Range("B1", AuxSh.Range("B" & Rows.Count).End(xlUp))
For Each b In GroupRg.SpecialCells(xlCellTypeVisible)
My_Range.AutoFilter Field:=2, Criteria1:="=" & b.Value
MainSh.Range("F2", MainSh.Range("F1").End(xlDown)).Copy
AuxSh.Range("C1").PasteSpecial Paste:=xlPasteValues
AuxSh.Range("C:C").RemoveDuplicates Columns:=1, Header:=xlNo
Set ClassRg = AuxSh.Range("C1", AuxSh.Range("C" & Rows.Count).End(xlUp))
For Each c In ClassRg.SpecialCells(xlCellTypeVisible)
My_Range.AutoFilter Field:=3, Criteria1:="=" & c.Value
MainSh.Range("K1", MainSh.Range("K" & Rows.Count).End(xlUp)).Copy _
Destination:=DestSh.Cells(1, Ccolumn)
My_Range.AutoFilter Field:=3 'Remove the AutoFilter
Ccolumn = Ccolumn + 1
Next c
ClassRg.ClearContents
My_Range.AutoFilter Field:=2 'Remove the AutoFilter
Next b
GroupRg.ClearContents
My_Range.AutoFilter Field:=1 'Remove the AutoFilter
Next a
End Sub
Best,
All your alternatives won't work if lr3 = 2, because Range("F2:F" & lr3).Value (.Value is invoked implicitly since you dont use Set) will NOT be an array but just a value, and the same applies for its Transpose.
The reason is that you are not using Set, so you are getting a value, and the value of a single cell will not be an array. I noticed that none of your Transpose operations is necessary. So try this quick-fix,
Remove all your Transpose statements and take the original range
use the Set keyword to have range objects instead of arrays
.
Set ca = Range("D2", Range("D" & Rows.Count).End(xlUp))
Set gr = Range("E2", Range("E" & Rows.Count).End(xlUp))
Set cl = Range("F2:F" & lr3)
That said, this will fix only the issue at hand. There are many other problems in the code. One of them being that when you apply My_Range.Parent.AutoFilterMode = False, All filters are removed, not only the one applied in the inner loop. But try fixing the current issue at the moment.

Resources