Filter by multiple values and multiple columns - excel

I am trying to make a custom filtering solution within an Excel file, but I'm not sure if it is possible.
I did some research on the Internet, and I came up with the solution from bellow, but when I run it gives me
run-time error '5': Invalid procedure call or argument.
Option Explicit
Sub Filtrare_date()
Dim Data_sh As Worksheet
Dim Raport_sh As Worksheet
Dim output_sh As Worksheet
Set Data_sh = ThisWorkbook.Sheets("Date")
Set Raport_sh = ThisWorkbook.Sheets("Raport")
Set output_sh = ThisWorkbook.Sheets("output")
output_sh.UsedRange.Clear
Data_sh.AutoFilterMode = False
'definim lista 1 de filtrare
Dim Filter_list() As String
Dim n As Integer
n = Application.WorksheetFunction.CountA(Raport_sh.Range("g:g")) - 2
ReDim Filter_list(n) As String
Dim i As Integer
For i = 0 To n
Filter_list(i) = Raport_sh.Range("g" & i + 2)
Next i
'definim lista 2 de filtrare
Dim Filter_list_2() As String
Dim m As Integer
m = Application.WorksheetFunction.CountA(Raport_sh.Range("h:h")) - 2
ReDim Filter_list(m) As String
Dim j As Integer
For j = 0 To m
Filter_list(j) = Raport_sh.Range("h" & j + 2)
Next j
'filtru dupa lista 1
Data_sh.UsedRange.AutoFilter 1, Filter_list(), xlFilterValues
'filtru dupa lista 2
Data_sh.UsedRange.AutoFilter 2, Filter_list_2(), xlFilterValues
Data_sh.UsedRange.SpecialCells(xlCellTypeVisible).Copy output_sh.Range("A1")
Data_sh.AutoFilterMode = False
MsgBox ("Selectia de date s-a terminat")
End Sub
The error is related to this line:
Data_sh.UsedRange.AutoFilter 2, Filter_list_2(), xlFilterValues

The following part had some errors:
m = Application.WorksheetFunction.CountA(Raport_sh.Range("h:h")) - 2
ReDim Filter_list_2(m) As String
Dim j As Integer
For j = 0 To m
Filter_list_2(j) = Raport_sh.Range("h" & j + 2)
Next j

The Worksheet Function transpose
is a useful method of building selection criteria from a range without looping. Also by using
With .. End With blocks
you can avoid specifying the name of the object multiple times. For example
Sub Filtrare_date()
Dim wsData As Worksheet, wsRaport As Worksheet, wsOutput As Worksheet
With ThisWorkbook
Set wsData = .Sheets("Date")
Set wsRaport = .Sheets("Raport")
Set wsOutput = .Sheets("output")
End With
wsOutput.Cells.Clear
wsData.AutoFilterMode = False
Dim ar1 As Variant, ar2 As Variant, i As Long
With wsRaport
i = .Range("G" & Rows.Count).End(xlUp).Row
ar1 = WorksheetFunction.Transpose(.Range("G3:G" & i).Value)
i = .Range("H" & Rows.Count).End(xlUp).Row
ar2 = WorksheetFunction.Transpose(.Range("H3:H" & i).Value)
End With
'Debug.Print Join(ar1, ","), Join(ar2, ","),
With wsData.UsedRange
.AutoFilter 1, ar1, xlFilterValues
.AutoFilter 2, ar2, xlFilterValues
.SpecialCells(xlCellTypeVisible).Copy wsOutput.Range("A1")
End With
wsData.AutoFilterMode = False
MsgBox ("Selectia de date s-a terminat"), vbInformation
End Sub

Related

In Excel Sheet how to Eliminate or Remove, Filter and copy the selected records defined in another sheet using dynamic array list (VBA Module)

I need the experts help as I am new in this area. I am trying to create the Dynamic array Macro for Excel sheet (VBA). In which I want to eliminate (delete or hide) the number of records on the bases of data selected in one particular column (“AlertCount”) in main Sheet “StatusReport” using dynamic array list.
Example : StatusReport (Worksheet)
Filter_Criteria (Worksheet)
Expected output :
All record should display without "1055" and "1056" related Alert Count (Eliminate Record)
But its removed all the records now instead of selected value
My Module as below it display the filter records only but I need to eliminate the selected filter records . VBA Module as below :
Sub DeleteFilter_Data()
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
Data_sh.AutoFilterMode = False
Dim AlertCount_List() As String
Dim n As Integer
n = Application.WorksheetFunction.CountA(Filter_Criteria.Range("A:A")) - 1
ReDim AlertCount_List(n) As String
Dim i As Integer
For i = 0 To n
AlertCount_List(i) = Filter_Criteria.Range("A" & i + 2)
Next i
Dim Arr01 As Variant
Dim i01 As Integer
Dim i02 As Integer
'Creates a list of everything in Column I, minus everything in Filter_Criteria list
Arr01 = Range("I2", Range("I2").End(xlDown))
For i01 = 1 To UBound(Arr01, 2)
For i02 = 0 To n - 1
If Arr01(i01, 1) = AlertCount_List(i02) Then
Arr01(i01, 1) = ""
End If
Next i02
Next i01
'Turns list into strings (needed for the Filter command).
Dim ListEdited() As String
ReDim ListEdited(1 To UBound(Arr01, 1)) As String
For i01 = 1 To UBound(Arr01, 2)
ListEdited(i01) = Arr01(i01, 1)
Next i01
'Filter command that keeps all entries except any found within the Filter_Criteria Sheet.
Data_sh.UsedRange.AutoFilter 9, ListEdited(), xlFilterValues
End Sub
Please help me out with corrected Macro using dynamic array list.
Thanks
Susheel
I think you are asking to keep all Alert_Counts except for the ones on the Filter_Criteria sheet? The code below does this. Please let me know if I have misunderstood your questions and I will try again.
EDIT 20210630: I have updated the below code.
Sub HideFilter_Data()
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
Data_sh.AutoFilterMode = False
Dim AlertCount_List() As String
Dim n As Integer
n = Application.WorksheetFunction.CountA(Filter_Criteria.Range("I:I")) - 1
ReDim AlertCount_List(n) As String
Dim i As Integer
For i = 0 To n
AlertCount_List(i) = Filter_Criteria.Range("I" & i + 2)
Next i
Dim Arr01 As Variant
Dim i01 As Integer
Dim i02 As Integer
'Creates a list of everything in Column I, minus everything in Filter_Criteria list
Arr01 = Range("I2", Range("I2").End(xlDown))
For i01 = 1 To UBound(Arr01, 1)
For i02 = 0 To n - 1
If Arr01(i01, 1) = AlertCount_List(i02) Then
Arr01(i01, 1) = ""
End If
Next i02
Next i01
'Turns list into strings (needed for the Filter command).
Dim ListEdited() As String
ReDim ListEdited(1 To UBound(Arr01, 1)) As String
For i01 = 1 To UBound(Arr01, 1)
ListEdited(i01) = Arr01(i01, 1)
Next i01
'Filter command that keeps all entries except any found within the Filter_Criteria Sheet.
Data_sh.UsedRange.AutoFilter 9, ListEdited(), xlFilterValues
'Data_sh.UsedRange.AutoFilter 9, AlertCount_List(), xlFilterValues
'Data_sh.UsedRange.AutoFilter 9, Criteria1:="<> 1056" ‘ This work fine but it's a hard coded value
End Sub
I have got the solution of how to Filter, Eleminate or Hide and Copy the Selected records to another worksheet. The list of Filter data defined in another worksheet and execute the Module by Button press events on the worksheet.
For Eliminate Data case we need to create the 2 list from main worksheet and another one for eliminate the records worksheet. And Compare the both the list and replace the matched case with null or blank in main sheet
Sub HideFilter_Data()
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
Data_sh.AutoFilterMode = False
Dim AlertCount_List() As String
Dim n As Integer
n = Application.WorksheetFunction.CountA(Filter_Criteria.Range("A:A")) - 1
ReDim AlertCount_List(n) As String
Dim i As Integer
For i = 0 To n
AlertCount_List(i) = Filter_Criteria.Range("A" & i + 2)
Next i
' Create the List of main worksheet
Dim Arr01 As Variant
Dim i01 As Integer
Dim i02 As Integer
Dim r As Integer
Dim r1 As Integer
r = Application.WorksheetFunction.CountA(Data_sh.Range("I:I")) - 2
ReDim StatusCount_List(r + 1) As String
For r1 = 0 To r
StatusCount_List(r1) = Data_sh.Range("I" & r1 + 2)
Next r1
'Creates a list of everything in Column I, minus everything in Filter_Criteria list
Dim str As Variant
Dim cnt As Integer
cnt = 0
' Executing the double loop for comparing both the List and eleminate the match data from the main sheet.
For Each Item In StatusCount_List
For Each subItem In AlertCount_List
If Item = subItem Then
StatusCount_List(cnt) = ""
End If
Next subItem
cnt = cnt + 1
Next Item
Data_sh.UsedRange.AutoFilter 9, StatusCount_List(), xlFilterValues
End Sub
Main Worksheet :
Eliminating Criteria (Hide the records)
Output (Eliminated / Hide/ Remove) as below:
Filter the selected records. Filter list defined in another worksheet.
If we need to select the selected record from the list of another sheet by using dynamic array.
Option Explicit ' Force explicit variable declaration.
Sub Filter_Data()
Dim Data_sh As Worksheet
Dim Filter_Criteria As Worksheet
Dim Output_Sh As Worksheet
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
Set Output_Sh = ThisWorkbook.Sheets("Output")
Output_Sh.UsedRange.Clear
Data_sh.AutoFilterMode = False
Dim AlertCount_List() As String
Dim n As Integer
n = Application.WorksheetFunction.CountA(Filter_Criteria.Range("A:A")) - 1
ReDim AlertCount_List(n) As String
Dim i As Integer
For i = 0 To n
AlertCount_List(i) = Filter_Criteria.Range("A" & i + 2)
Next i
Data_sh.UsedRange.AutoFilter 9, AlertCount_List(), xlFilterValues
End Sub
Output :
Copy the selected records to new worksheet. Filter list defined in another worksheet.
Option Explicit ' Force explicit variable declaration.
Sub CopyFilter_Data()
Dim Data_sh As Worksheet
Dim Filter_Criteria As Worksheet
Dim Output_Sh As Worksheet
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
Set Output_Sh = ThisWorkbook.Sheets("Output")
Output_Sh.UsedRange.Clear
Data_sh.AutoFilterMode = False
Dim AlertCount_List() As String
Dim n As Integer
n = Application.WorksheetFunction.CountA(Filter_Criteria.Range("A:A")) - 1
ReDim AlertCount_List(n) As String
Dim i As Integer
For i = 0 To n
AlertCount_List(i) = Filter_Criteria.Range("A" & i + 2)
Next i
'Data_sh.UsedRange.AutoFilter 9, Array("1055", "1056"), xlFilterValues
Data_sh.UsedRange.AutoFilter 9, AlertCount_List(), xlFilterValues
Data_sh.UsedRange.Copy Output_Sh.Range("A1")
Data_sh.AutoFilterMode = False
'MsgBox ("Data has been copied")
End Sub
Output :
Please, try the next code. As I said (twice) in my comments it is not possible to filter more than two "not equal to" type conditions. So, it solves the problem as you presented in your question (two conditions):
Sub filterCriteriaArray()
Dim Data_sh As Worksheet, Filter_Criteria As Worksheet, lastR As Long, arrC()
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
lastR = Filter_Criteria.Range("A" & Filter_Criteria.rows.count).End(xlUp).row
arrC = Filter_Criteria.Range("A2:A" & lastR).value
Data_sh.UsedRange.AutoFilter field:=9, Criteria1:="<>" & arrC(1, 1), Operator:=xlAnd, Criteria2:="<>" & arrC(2, 1)
End Sub
Edited:
The next code version uses AdvancedFilter, which allows using more criteria of the type you need, but it does not uses array as criteria. I used a trick, creating a range in a newly add sheet (hidden), based on the array extracted from your criteria sheet:
Sub filterCriteriaFromArray()
Dim Data_sh As Worksheet, Filter_Criteria As Worksheet, crit As Worksheet, lastR As Long, arrCr()
Dim strHeader As String, filtRng As Range, rngCrit As Range, i As Long
strHeader = "Head8" ' "AlertCount" 'important the be the correct header (of I:I column)
Set Data_sh = ActiveSheet 'ThisWorkbook.Sheets("StatusReport")
Set filtRng = Data_sh.Range(Data_sh.Range("A1"), _
Data_sh.cells(Data_sh.UsedRange.rows.count, Data_sh.cells(1, Data_sh.Columns.count).End(xlToLeft).Column))
Set Filter_Criteria = Data_sh.Next 'ThisWorkbook.Sheets("Filter_Criteria")
lastR = Filter_Criteria.Range("A" & Filter_Criteria.rows.count).End(xlUp).row 'last row in Filter_Criteria
arrCr = Filter_Criteria.Range("A2:A" & lastR).value 'put criteria values in the array
On Error Resume Next
Set crit = Sheets("CriteriaSh") 'check if sheets "CriteriaSh" exists
If err.Number <> 0 Then
err.Clear 'if it does not exist, it is created
Set crit = Data_sh.Parent.Sheets.Add(After:=Worksheets(Sheets.count))
crit.Name = "CriteriaSh"
crit.Visible = xlSheetVeryHidden
Else
crit.cells.ClearContents 'if it exists its cells are cleared
End If
On Error GoTo 0
For i = 1 To UBound(arrCr) 'Build the range to be used in AdvancedFilter criteria
crit.cells(1, i).value = strHeader
crit.cells(2, i).value = "<>" & arrCr(i, 1)
Next i
'set the criteria range:
Set rngCrit = crit.Range(crit.Range("A1"), crit.cells(2, UBound(arrCr)))
filtRng.AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:=rngCrit, Unique:=False
End Sub

VBA - Multi wildcard filter using array values

Hello VBA Developers,
I am having a hard time solving a multi-wildcard filter for criteria(s) listed in an array. The code stops at "vTst = Doc_ID_Arr(i)", stating vTst = Empty. However, checking Doc_ID_Arr is not empty if you check the debugger.
Sub doc_id()
'Segment 1 ----
'Get the worksheet called "LOB Docs"
Dim sh_1 As Worksheet
Set sh_1 = ThisWorkbook.Worksheets("LOB Docs")
' Declare an array to hold all LOB Doc ID numbers
Dim Doc_ID_Arr As Variant
Dim Doc_ID_Value As String
Dim j As Long
Dim i As Long
With sh_1
lastrow_Header_Config = sh_1.Cells(Rows.count, "A").End(xlUp).Row
' Read LOB DOC ID's from Column Cell A2 to last value in Column A
ReDim Doc_ID_Arr(Application.WorksheetFunction.CountA(sh_1.Range("A2:A" & lastrow_Header_Config)) - 1) As Variant
j = 0
For i = 2 To lastrow_Header_Config
Doc_ID_Value = sh_1.Range("A" & i).Value
If Doc_ID_Value <> "" Then
Doc_ID_Arr(j) = "*" & Doc_ID_Value & "*"
j = j + 1
End If
Next
End With
' ' Debug.Print "Doc_ID_Value"
' For i = LBound(Doc_ID_Arr) To UBound(Doc_ID_Arr)
' Debug.Print Doc_ID_Arr(i)
' Next i
'Segment 2 ----
Dim sh_2 As Worksheet 'Data Sheet
Dim sh_3 As Worksheet 'Output Sheet
Set sh_2 = ThisWorkbook.Worksheets("GDL db") 'Data Sheet
Set sh_3 = ThisWorkbook.Worksheets("Seed Template Output")
Dim Dic As Object
Dim eleData As Variant
Dim eleCrit As Variant
Dim ArrData As Variant
Dim vTst As Variant
Set Dic = CreateObject("Scripting.Dictionary")
Dim x As Long
For x = LBound(Doc_ID_Arr) To UBound(Doc_ID_Arr)
vTst = Doc_ID_Arr(i)
Next x
With sh_2
.AutoFilterMode = False
ArrData = .Range("A1:A" & .Cells(.Rows.count, "A").End(xlUp).Row)
For Each eleCrit In vTst
For Each eleData In ArrData
If eleData Like eleCrit Then _
Dic(eleData) = vbNullString
Next
Next
.Columns("A:A").AutoFilter Field:=1, Criteria1:=Dic.Keys, Operator:=xlFilterValues
sh_2.UsedRange.Copy sh_3.Range("A1")
End With
End Sub
I am trying to filter sh_2, Column A for each value(individual) or all values(en masse) that is placed in the Doc_ID_Arr created in Segment 1. The target is to place each filter output for each ID onto sh_3, without overwriting previous placed values/rows.
Using your previously-posted sample workbook this works for me:
Sub document_link_extract()
'Define data source
Dim GDL_Data As Worksheet 'Datasheet holding Docs links
Dim LOB_Doc As Worksheet 'Docs to filter for
Dim Doc_Output_sh As Worksheet 'Seed Template - curated document list
Dim Doc_ID_List() As String, v, rngIds As Range
Dim arrVals, arrSearch, dict, rwV As Long, rwS As Long, srch
Set GDL_Data = ThisWorkbook.Sheets("Sheet2") 'DataSheet
Set LOB_Doc = ThisWorkbook.Sheets("Sheet1") 'Filter Criteria Sheet
Set Output_sht = ThisWorkbook.Sheets("Sheet3") 'Output for' Look 1/2 - URL Check & PDF Extract
Output_sht.UsedRange.Clear
'get array of search terms
With LOB_Doc
arrSearch = .Range(.Range("A2"), .Cells(.Rows.Count, 1).End(xlUp)).Value
End With
'get array of data column values
With GDL_Data
arrVals = .Range(.Range("A2"), .Cells(.Rows.Count, 1).End(xlUp)).Value
End With
Set dict = CreateObject("scripting.dictionary")
'loop over each search term
For rwS = 1 To UBound(arrSearch, 1)
srch = "*" & arrSearch(rwS, 1) & "*" '<< search term with wildcards
'loop over each value
For rwV = 1 To UBound(arrVals, 1)
v = arrVals(rwV, 1)
'if value matches search term then add to dictionary
If v Like srch Then dict(v) = True
Next rwV
Next rwS
GDL_Data.AutoFilterMode = False 'if there is any filter, remove it
'filter using the dictionary keys array
GDL_Data.UsedRange.AutoFilter 1, dict.keys, xlFilterValues
GDL_Data.UsedRange.Copy Output_sht.Range("A1")
GDL_Data.AutoFilterMode = False
End Sub

Need to make excel vba vlookup more efficient

I'm redesigning some finance reports for my organization to move away from a 3rd party software and looking to use VBA to assist in the automation. Haven't written VBA since college, so a little rusty.
I've gotten the code to work, however it's very inefficient and is running at about 1000k records every 30 seconds, which is not feasible with a few hundred thousand records. I've tried several different options that you all have posted in different threads, but must be missing something.
Can you please take a look?
Most threads I've looked at have referenced either a direct input via single cell or same sheet to perform the lookup. This is a single column on Sheet A (ATB-Allowance Reserving-Calc) and then find lookups in table on Sheet B (Plan Global Lookups).
I do want it to skip over errors, and return nothing.
I've tried the fill down method and copy and paste, neither of which I can get to work with a formula. They just seem to want to fill with the value from the original formula.
I'm thinking it's not working due to jumping back and forth between sheets, which I've encountered issues with in different calculations.
I'm not one to just try one or two times, so this is definitely me at the end of my rope.
Dim GlobalExpPct As Variant
Range("AI2").Select 'Gets historical rates from Plan Global Lookups tab
Do
On Error Resume Next
GlobalExpPct = Application.WorksheetFunction.VLookup(ActiveCell.Offset(0, -24), Sheets("Plan Global Lookups").Range("A:B"), 2, False)
ActiveCell.value = GlobalExpPct
GlobalExpPct = vbNullString
ActiveCell.Offset(1, 0).Select
Loop While ActiveCell.Row < 1000 'have this in place to keep it from looping through all the records
I suspect the slow processing is due to selecting of the next cell each time, and then essentially calling the worksheet values and formula again. I'm typically seeing that the formula is returning either null value or getting the same value from the previous formula in the fill down.
Thanks for the help in advance. This is a great resource as I've been able to solve 99% of my issues so far on this site.
Edit
This code provided by Ahmed are working great, but I need one more criteria:
If an additional column ("T" Account Base Class) is "IP", then we can pull from the "Plan Global Lookups A:B" as currently setup. However, if it's populated otherwise, we'll need to pull from a lookup on another column. We can duplicate the table on the same sheet or still use column A as the lookup for the plan, whichever is the most efficient. Here is the code as it stands today which is working perfectly:
Sub GetGlobals()
Dim IntervalProcessing60k As Integer
Dim SRow As Long
Dim ERow As Long
Dim Src As Variant
Dim AcctPlan
Dim GlobalExpPct As Variant
Dim AcctPlanRng As Range
Dim Rslt() As Variant
Dim t As Date
Dim GetGlobalTime As Date
Dim ActWs As Worksheet
Dim ATBAllowResCalc As Worksheet
Set ActWs = ThisWorkbook.ActiveSheet
Set PlanGlobalWs = ThisWorkbook.Sheets("Plan Global Lookups")
Set ATBAllowResCalc = ThisWorkbook.Sheets("ATB-Allowance Reserving-Calc")
Set AcctGlobalRng = PlanGlobalWs.Range("A1:B" & PlanGlobalWs.Cells(PlanGlobalWs.Rows.Count, 1).End(xlUp).Row)
t = Now()
LastRow = Range("A" & Rows.Count).End(xlUp).Row
IntervalProcessing60k = 0
SRow = 2
ERow = LastRow
Src = ActWs.Range("K" & SRow & ":K" & ERow).value
X = 1
For Rw = SRow To ERow
AcctPlan = Src(Rw - SRow + 1, 1)
On Error Resume Next
GlobalExpPct = Application.WorksheetFunction.VLookup(AcctPlan, AcctGlobalRng, 2, False)
On Error GoTo 0
ReDim Preserve Rslt(1 To X)
Rslt(X) = IIf(IsNull(GlobalExpPct), "", GlobalExpPct)
GlobalExpPct = vbNullString
If Rw > 120000 Then Debug.Print Rw, X, Src(Rw - SRow + 1, 1), Rslt(X)
If X = 60000 Then
ActWs.Range("AI" & IntervalProcessing60k * 60000 + SRow).Resize(UBound(Rslt, 1), 1).value = Application.Transpose(Rslt)
IntervalProcessing60k = IntervalProcessing60k + 1
X = 1
ReDim Rslt(1 To 1)
Else
X = X + 1
End If
Next Rw
ActWs.Range("AI" & IntervalProcessing60k * 60000 + SRow).Resize(UBound(Rslt, 1), 1).value = Application.Transpose(Rslt)
GetGlobalTime = Format(Now() - t, "hh:mm:ss")
End Sub
May try this and see if performance improves
Sub testModified()
Dim GlobalExpPct As Variant, Rng As Range, Rw As Long
Dim ValtoLook, Ws As Worksheet, ActWs As Worksheet, tm As Double
tm = Timer
Set ActWs = ThisWorkbook.ActiveSheet
Set Ws = ThisWorkbook.Sheets("Plan Global Lookups")
'Set Rng = Ws.Range("A:B")
'this would be more efficent
Set Rng = Ws.Range("A1:B" & Ws.Cells(Ws.Rows.Count, 1).End(xlUp).Row)
For Rw = 2 To 1000
ValtoLook = ActWs.Range("AI" & Rw).Offset(0, -24).Value
On Error Resume Next
GlobalExpPct = Application.WorksheetFunction.VLookup(ValtoLook, Rng, 2, False)
On Error GoTo 0
Range("AI" & Rw).Value = GlobalExpPct
GlobalExpPct = vbNullString
Next Rw
Debug.Print " Time in second " & Timer - tm; ""
End Sub
if i have not correctly guessed the columns and ranges you are working with, may kindly modify them to your requirement.
It could be made efficient if you confirm there is all the values of Column K and AI are values and they are not inter dependent with some formulas etc. the above code may prove sufficient for 1000 rows. But for heavy files with 10-1000 K rows, the code required to be more efficient. in that case Excel cell operations are to be minimized by using array. Adding above code modified with Array
Sub testModifiedArray()
Dim GlobalExpPct As Variant, Rng As Range, Rw As Long
Dim ValtoLook, Ws As Worksheet, ActWs As Worksheet
Dim Rslt() As Variant, Src As Variant, tm As Double
tm = Timer
Set ActWs = ThisWorkbook.ActiveSheet
Set Ws = ThisWorkbook.Sheets("Plan Global Lookups")
'Set Rng = Ws.Range("A:B")
'next line would be more efficent, You may define range directly if you know the end row
Set Rng = Ws.Range("A1:B" & Ws.Cells(Ws.Rows.Count, 1).End(xlUp).Row)
Src = ActWs.Range("K2:K1000").Value
For Rw = 2 To 1000
ValtoLook = Src(Rw - 1, 1)
On Error Resume Next
GlobalExpPct = Application.WorksheetFunction.VLookup(ValtoLook, Rng, 2, False)
On Error GoTo 0
ReDim Preserve Rslt(1 To Rw - 1)
Rslt(Rw - 1) = IIf(IsNull(GlobalExpPct), "", GlobalExpPct)
'Debug.Print Rslt(Rw - 1)
GlobalExpPct = vbNullString
Next Rw
ActWs.Range("AI2").Resize(UBound(Rslt, 1), 1).Value = Application.Transpose(Rslt)
Debug.Print " Time in second " & Timer - tm; ""
End Sub
Both the code tested with my Guess of Column and ranges. As I personally don't prefer to keep calculations, event processing and screen updating off (in normal cases) i haven't added that standard lines. However you may use these standard techniques, depending on the working file condition.
Edit: modified to accommodate overcome 65K limit of array transpose limt
Option Explicit
Sub testModifiedArray2()
Dim GlobalExpPct As Variant, rng As Range, Rw As Long
Dim ValtoLook, Ws As Worksheet, ActWs As Worksheet
Dim Rslt() As Variant, Src As Variant, tm As Double
Dim Chunk60K As Integer, X As Long, SRow As Long, ERow As Long
tm = Timer
Set ActWs = ThisWorkbook.ActiveSheet
Set Ws = ThisWorkbook.Sheets("Plan Global Lookups")
'Set Rng = Ws.Range("A:B")
'next line would be more efficent, You may define range directly if you know the end row
Set rng = Ws.Range("A1:B" & Ws.Cells(Ws.Rows.Count, 1).End(xlUp).Row)
Chunk60K = 0
SRow = 2
ERow = 120030
Src = ActWs.Range("K" & SRow & ":K" & ERow).Value
X = 1
For Rw = SRow To ERow
ValtoLook = Src(Rw - SRow + 1, 1)
On Error Resume Next
GlobalExpPct = Application.WorksheetFunction.VLookup(ValtoLook, rng, 2, False)
On Error GoTo 0
ReDim Preserve Rslt(1 To X)
Rslt(X) = IIf(IsNull(GlobalExpPct), "", GlobalExpPct)
GlobalExpPct = vbNullString
If Rw > 120000 Then Debug.Print Rw, X, Src(Rw - SRow + 1, 1), Rslt(X)
If X = 60000 Then
ActWs.Range("AI" & Chunk60K * 60000 + SRow).Resize(UBound(Rslt, 1), 1).Value = Application.Transpose(Rslt)
Chunk60K = Chunk60K + 1
X = 1
ReDim Rslt(1 To 1)
Else
X = X + 1
End If
Next Rw
ActWs.Range("AI" & Chunk60K * 60000 + SRow).Resize(UBound(Rslt, 1), 1).Value = Application.Transpose(Rslt)
Debug.Print " Time in second " & Timer - tm; ""
End Sub
Last Answer modified for improved efficiency and new requirement, Test time to process around 120 K rows is around 6 seconds only. additionally column "T" is tested for a value "IP" and lookup value pulled up from column B or C accordingly.
Option Explicit
Sub GetGlobals()
Dim SRow As Long
Dim ERow As Long
Dim Src As Variant, Src2 As Variant
Dim AcctPlan
Dim GlobalExpPct As Variant
Dim AcctPlanRng As Range
Dim Rslt() As Variant
Dim tm As Double
Dim ActWs As Worksheet, PlanGlobalWs As Worksheet
Dim AcctGlobalRng As Range
Dim ATBAllowResCalc As Worksheet
Dim LastRow As Long, X As Long, Rw As Long
Dim LookArr As Variant, LookUpCol As Integer
Set ActWs = ThisWorkbook.ActiveSheet
Set PlanGlobalWs = ThisWorkbook.Sheets("Plan Global Lookups")
'Set ATBAllowResCalc = ThisWorkbook.Sheets("ATB-Allowance Reserving-Calc")
Set AcctGlobalRng = PlanGlobalWs.Range("A1:C" & PlanGlobalWs.Cells(PlanGlobalWs.Rows.Count, 1).End(xlUp).Row)
LookArr = AcctGlobalRng.Value
tm = Timer
LastRow = Range("K" & Rows.Count).End(xlUp).Row
SRow = 2
ERow = LastRow
Src = ActWs.Range("K" & SRow & ":K" & ERow).Value
Src2 = ActWs.Range("T" & SRow & ":T" & ERow).Value
ReDim Rslt(1 To ERow - SRow + 1, 1 To 1)
For Rw = SRow To ERow
AcctPlan = Src(Rw - SRow + 1, 1)
GlobalExpPct = ""
For X = 1 To UBound(LookArr, 1)
If AcctPlan = LookArr(X, 1) Then
LookUpCol = IIf(Src2(Rw - SRow + 1, 1) = "IP", 2, 3)
GlobalExpPct = LookArr(X, LookUpCol)
Exit For
End If
Next X
GlobalExpPct = IIf(IsNull(GlobalExpPct), "", GlobalExpPct)
Rslt(Rw - SRow + 1, 1) = GlobalExpPct
Next Rw
ActWs.Range("AI" & SRow).Resize(UBound(Rslt, 1), 1).Value = Rslt
Debug.Print " Time in second " & Timer - tm; ""
End Sub

Exceeding row limit - create new sheet

I have 2 columns on a sheet "list", one column that lists all business entities, the other lists all org units. The functionality of the code below works perfectly but returns an error because it exceeds the sheet row limit.
The data is pasted onto a sheet "cc_act" is there a way to at point of error create a new sheet called "cc_act1"...."cc_act2" until the script is complete?
Declare Function HypMenuVRefresh Lib "HsAddin" () As Long
Sub cc()
Application.ScreenUpdating = False
Dim list As Worksheet: Set list = ThisWorkbook.Worksheets("list")
Dim p As Worksheet: Set p = ThisWorkbook.Worksheets("p")
Dim calc As Worksheet: Set calc = ThisWorkbook.Worksheets("calc")
Dim cc As Worksheet: Set cc = ThisWorkbook.Worksheets("cc_act")
Dim cc_lr As Long
Dim calc_lr As Long: calc_lr = calc.Cells(Rows.Count, "A").End(xlUp).Row
Dim calc_lc As Long: calc_lc = calc.Cells(1,
calc.Columns.Count).End(xlToLeft).Column
Dim calc_rg As Range
Dim ctry_rg As Range
Dim i As Integer
Dim x As Integer
list.Activate
For x = 2 To Range("B" & Rows.Count).End(xlUp).Row
If list.Range("B" & x).Value <> "" Then
p.Cells(17, 3) = list.Range("B" & x).Value
End If
For i = 2 To Range("A" & Rows.Count).End(xlUp).Row
If list.Range("A" & i).Value <> "" Then
p.Cells(17, 4) = list.Range("A" & i).Value
p.Calculate
End If
p.Activate
Call HypMenuVRefresh
p.Calculate
'''changes country on calc table
calc.Cells(2, 2) = p.Cells(17, 4)
calc.Cells(2, 3) = p.Cells(17, 3)
calc.Calculate
'''copy the calc range and past under last column
With calc
Set calc_rg = calc.Range("A2:F2" & calc_lr)
End With
With cc
cc_lr = cc.Cells(Rows.Count, "A").End(xlUp).Row + 1
calc_rg.Copy
cc.Cells(cc_lr, "A").PasteSpecial xlPasteValues
End With
Next i
Next x
Application.ScreenUpdating = True
End Sub
I suppose there are a few ways to handle something like this. See the code sample below, and adapt it to your specific needs.
Sub LongColumnToAFewColumns()
Dim wsF As Worksheet, WST As Worksheet
Dim rf As Range, rT As Range
Dim R As Long, j As Integer
' initialize
Set wsF = ActiveSheet
Set WST = Sheets.Add
WST.Name = "Results"
j = 1
For R = 1 To wsF.Cells(Rows.Count, 1).End(xlUp).Row Step 65536
wsF.Cells(R, 1).Resize(65536).Copy
WST.Cells(j, 1).PasteSpecial xlPasteValues
WST.Cells(j, 1).PasteSpecial xlPasteValues
j = j + 1
Next R
End Sub
As an aside, you may want to consider using MS Access for this kind of thing. Or, better yet, Python or even R. Good luck with your project.

Excel 2013 Overflow due to lack of VBA optimization

I would like to export data from a consolidated sheet (DATA) to multiple sheets regarding criteria.
I have a total of 13 criteria, each criteria has to be exported in its dedicated sheet.
I'm trying to optimize this macro (only 2 criteria here) because it lag out
Sub copy()
Application.ScreenUpdating = False
Dim i As Long
Dim j As Long
Dim sh As Worksheet
Dim feuillePrincipale As Worksheet
Dim S01Sheet As Worksheet
Dim S02Sheet As Worksheet
Set feuillePrincipale = ThisWorkbook.Sheets("DATA")
Set S01Sheet = ThisWorkbook.Sheets("S01")
Set S02Sheet = ThisWorkbook.Sheets("S02")
For Each sh In ThisWorkbook.Worksheets
If sh.Name = "S01" Then
i = 2
j = 2
While Not IsEmpty(feuillePrincipale.Cells(i, 1))
If feuillePrincipale.Cells(i, 11).Value Like "S01*" Then
feuillePrincipale.Cells.Rows(i).EntireRow.copy S01Sheet.Rows(j)
j = j + 1
End If
i = i + 1
Wend
End If
If sh.Name = "S02" Then
i = 2
j = 2
While Not IsEmpty(feuillePrincipale.Cells(i, 1))
If feuillePrincipale.Cells(i, 11).Value Like "S02*" Then
feuillePrincipale.Cells.Rows(i).EntireRow.copy S02Sheet.Rows(j)
j = j + 1
End If
i = i + 1
Wend
End If
Next
Application.ScreenUpdating = True
End Sub
If you have any idea, I read I can use Advanced filter but as you guess I'm new in VBA so I'm listening any tips!
Here is the Advanced Filter method you asked for:
Public Sub Christophe()
Const FILTER_COLUMN = 11
Dim i&, rCrit As Range, rData As Range, aShts
aShts = ["SO"&row(1:13)]
Set rData = Sheets("DATA").[a1].CurrentRegion
Set rCrit = rData.Resize(2, 1).Offset(, rData.Columns.Count + 2)
rCrit(1) = rData(1, FILTER_COLUMN)
For i = 1 To UBound(aShts)
rCrit(2) = aShts(i, 1) & "*"
rData.AdvancedFilter xlFilterCopy, rCrit, Sheets(aShts(i, 1)).[a1].Resize(, rData.Columns.Count)
Next
rCrit.Clear
End Sub
The execution time should be instantaneous.
Note: this assumes that you do have 13 criteria, each starting with "SO" and that they occupy column 11 of the Data sheet. It also assumes that you already have 13 sheets named SO1... SO13 in the workbook.
UPDATE
Based on new information that the pattern of the criteria can change, please try this version instead. Note, that it assumes that the sheets already exist and that the sheet names match the criteria:
Public Sub Christophe()
Const FILTER_COLUMN = 11
Dim i&, rCrit As Range, rData As Range, aShts
aShts = Array("SO1", "SO2", "ADQ03", "LocS10")
Set rData = Sheets("DATA").[a1].CurrentRegion
Set rCrit = rData.Resize(2, 1).Offset(, rData.Columns.Count + 2)
rCrit(1) = rData(1, FILTER_COLUMN)
For i = 0 To UBound(aShts)
rCrit(2) = aShts(i) & "*"
rData.AdvancedFilter xlFilterCopy, rCrit, Sheets(aShts(i)).[a1].Resize(, rData.Columns.Count)
Next
rCrit.Clear
End Sub
Try using an array to set your criteria sheets:
Dim shArray As Variant
Dim shArrayString As String
Dim feuillePrincipale As Excel.Worksheet
Dim i As Long
Dim j As Long
Set feuillePrincipale = ThisWorkbook.Sheets("DATA")
j = 1
'// Create array and populate
shArray = Array("S01", "S02", "S03", "S04") '// add as required
'// Create string representation of array
shArrayString = "{"""
For i = LBound(shArray) To UBound(shArray)
shArrayString = shArrayString & shArray(i) & ""","""
Next
shArrayString = Left(shArrayString, Len(shArrayString) - 2) & "}"
'//Start loop
With feuillePrincipale
For i = 2 To .Cells(.Rows.Count, 1).End(xlUp).Row
If Not Evaluate("ISERROR(MATCH(" & Left(.Cells(i, 11), 3) & "," & shArrayString & ",0))") Then
.Rows(i).Copy Sheets(shArray(WorksheetFunction.Match(Left(.Cells(i, 11), 3), shArray, 0))).Cells(j, 1)
j = j + 1
End If
Next
End With
It's a bit unclear because if you follow the code you've posted - it's actually just copying and pasting data to the same sheet...
Yes, you should use an autofilter and use a special select to get only the visible cells.
If you want the loop method, you should loop through each row on sheets("DATA") and use a Select Case Statement to decide onto which sheet the data is placed.
By looping through each sheet you are adding loops that will slow it down.
Application.ScreenUpdating = False
Dim i As Long
Dim j As Long
Dim cel As Range
Dim sh As Worksheet
Dim feuillePrincipale As Worksheet
Dim S01Sheet As Worksheet
Dim S02Sheet As Worksheet
Set feuillePrincipale = ThisWorkbook.Sheets("DATA")
Set S01Sheet = ThisWorkbook.Sheets("S01")
Set S02Sheet = ThisWorkbook.Sheets("S02")
For Each cel In feuillePrincipale.Range(feuillePrincipale.Range("A1"), feuillePrincipale.Range("A1").End(xlDown))
Select Case Left(cel.offset(,10).value, 3)
Case "S01"
j = S01Sheet.Range("A" & Rows.count).End(xlUp).Offset(1).Row
feuillePrincipale.Cells.Rows(cel.Row).EntireRow.copy S01Sheet.Rows(j)
Case "S02"
j = S02Sheet.Range("A" & Rows.count).End(xlUp).Offset(1).Row
feuillePrincipale.Cells.Rows(cel.Row).EntireRow.copy S02Sheet.Rows(j)
'Case .... keep adding select statement till you get to the last condition
Case Else
End Select
Next cel
Application.ScreenUpdating = True

Resources