Loop Array from UBound to LBound and check values - excel

I'm trying to create an array, loop from UBound to LBound and check values with the below code.
I 'm receiving an error on line:
If arrPart(i) = strResult Then
Run time error 9
The range I try to import in array:
Code:
Option Explicit
Sub ArrayTest()
Dim LastColumn As Long, CounterPart As Long, i As Long
Dim arrPart As Variant
Dim strResult As String
With ThisWorkbook.Worksheets("Sheet1")
LastColumn = .Cells(1, .Columns.Count).End(xlToLeft).Column
strResult = "N"
'Set as an array the 4 last matches
arrPart = .Range(Cells(1, LastColumn - 3), Cells(1, LastColumn))
CounterPart = 0
For i = UBound(arrPart) To LBound(arrPart) Step -1
If arrPart(i) = strResult Then
CounterPart = CounterPart + 1
Else
Exit For
End If
Next
End With
End Sub
any suggestions?

Per all the comments above:
Option Explicit
Sub ArrayTest()
Dim LastColumn As Long, CounterPart As Long, i As Long
Dim arrPart As Variant
Dim strResult As String
With ThisWorkbook.Worksheets("Sheet1")
LastColumn = .Cells(1, .Columns.Count).End(xlToLeft).Column
strResult = "N"
'Set as an array the 4 last matches
arrPart = .Range(.Cells(1, 1), .Cells(1, LastColumn))
CounterPart = 0
For i = UBound(arrPart, 2) To LBound(arrPart, 2) Step -1
If arrPart(1, i) = strResult Then
CounterPart = CounterPart + 1
Else
Exit For
End If
Next
End With
Debug.Print CounterPart
End Sub

Suppose you have a table of cells starting from B4.
This is how you find out the size of the table, transfer the values into an array and iterate through them.
Public Sub ArrayTest()
Dim r_start As Range
Set r_start = Range("B4")
Dim i As Long, n As Long
n = Range(r_start, r_start.End(xlToRight)).Columns.Count
Dim arrPart() As Variant
arrPart = r_start.Resize(1, n).Value
Dim strResult As String
strResult = "N"
Dim counter As Long
counter = 0
For i = 1 To n
If arrPart(1, i) = strResult Then
counter = counter + 1
Else
Exit For
End If
Next i
Debug.Print counter
End Sub

Related

Fillfdown Approach for an index match function via VBA

with the given code I am trying hard incorporate the Filldown approach until the last row but at present whatever I do only fills row number 1:
Sub FillDownApproach()
Dim destinationWs As Worksheet
Dim destinationLastRow As Long
Set destinationWs = ThisWorkbook.Worksheets("Main Board")
destinationLastRow = destinationWs.Range("A" & Rows.Count).End(xlUp).Row
RetVal = destinationWs.Evaluate("INDEX('MyData'!$E:$E,MATCH(1,($A2='MyData'!$B:$B)*(""MyItem""='MyData'!$D:$D),0))")
destinationWs.Range("C2").Value = RetVal
destinationWs.Range("C3: " & "C" & destinationLastRow).FillDown
End Sub
Any suggestion that could point towards the right direction.
Thanks
You cannot do what you want without looping. And Looping ranges is slow.
Instead load Variant arrays and loop them.
Sub FillDownApproach()
Dim destinationWs As Worksheet
Set destinationWs = ThisWorkbook.Worksheets("Main Board")
Dim destinationLastRow As Long
destinationLastRow = destinationWs.Range("A" & Rows.Count).End(xlUp).Row
Dim lkpArr As Variant
lkpArr = destinationWs.Range("A2:A" & destinationLastRow).Value
With Worksheets("MyData")
Dim retval As Variant
retval = Intersect(.Range("E:E"), .UsedRange)
Dim mtch As Variant
mtch = Intersect(.Range("B:D"), .UsedRange)
End With
Dim outArr As Variant
ReDim outArr(1 To UBound(lkpArr, 1), 1 To 1)
Dim i As Long
For i = 1 To UBound(lkpArr, 1)
Dim j As Long
For j = 1 To UBound(retval, 1)
If mtch(j, 3) = "MyItem" Then
If mtch(j, 1) = lkpArr(i, 1) Then
outArr(i, 1) = retval(j, 1)
Exit For
End If
End If
Next j
Next i
destinationWs.Range("C2").Resize(UBound(outArr, 1), 1).Value = outArr
End Sub

VBA: faster way to compare two ranges?

I need to compare two ranges and see if value in one range appears in the other. This is the code I use:
Dim rng1 As Range
Dim rng2 As Range
Dim cell as Range
Dim found as Range
set rng1 = ....
set rng2 = ....
for each cell in rng1
set found = rng2.Find(what:=cell,.....
Next cell
This code is OK if the range is in thousands of rows, single column. When it comes to tens of thousands, it's very slow.
Anyway to speed it up?
This might be the fastest way for large amounts of data:
Option Explicit
Sub Test()
Dim rng1 As Range
Set rng1 = YourShorterRange
Dim rng2 As Range
Set rng2 = YourLargerRange
Dim C As Range
Dim Matches As Object: Set Matches = CreateObject("Scripting.Dictionary")
'input the larger data inside a dictionary
For Each C In rng2
If Not Matches.Exists(C.Value) Then Matches.Add C.Value, 1
Next C
Dim i As Long
Dim arr As Variant
'input the shorter data inside an array
arr = rng1.Value
For i = 1 To UBound(arr)
If Matches.Exists(arr(i, 1)) Then
'your code if the value is found
End If
Next i
End Sub
Edit for Dorian:
Option Explicit
Sub Test()
Dim rng1 As Range
Set rng1 = YourShorterRange
Dim rng2 As Range
Set rng2 = YourLargerRange
Dim i As Long, j As Long
Dim arr As Variant
Dim Matches As Object: Set Matches = CreateObject("Scripting.Dictionary")
arr = rng1.Value
'input the larger data inside a dictionary
For i = 1 To UBound(arr)
For j = 1 To UBound(arr, 2)
If Not Matches.Exists(arr(i, j)) Then Matches.Add arr(i, j), 1
Next j
Next i
'input the shorter data inside an array
arr = rng2.Value
For i = 1 To UBound(arr)
For j = 1 To UBound(arr, 2)
If Matches.Exists(arr(i, j)) Then
'your code if the value is found
End If
Next j
Next i
End Sub
Maybe something along these lines:
Sub Test()
Dim arr1 As Variant, arr2 As Variant
Dim arrList As Object: Set arrList = CreateObject("System.Collections.ArrayList")
Dim x As Long
arr1 = rng1 'Specify your range
arr2 = rng2 'Specify your range
For x = LBound(arr2) To UBound(arr2)
arrList.Add arr2(x, 1)
Next x
For x = LBound(arr1) To UBound(arr1)
If arrList.contains(arr1(x, 1)) = True Then
Debug.Print arr1(x, 1) & " contained within range 2"
End If
Next x
End Sub
I would suggest you :
Application.match
You can also look here you will find an interesting studies on 3 different ways of Search. Those 3 Different way will be studied By Time and By number of occurences.
According Fastexcel the conclusion of this study is :
Don’t use Range.Find unless you want to search a large number of
columns for the same thing (you would have to do a Match for each
column).
The Variant array approach is surprisingly effective,
particularly when you expect a large number of hits.
Match wins easily
for a small number of hits.
So If you except a large number of hit you might have to give a try variant array method. The 3 ways are listed in Fastexcel tuto
Edit
After reading some comment I did a new test :
Variant code
Sub Test1()
Dim vArr As Variant
Dim j As Long
Dim n As Long
Dim dTime As Double
dTime = MicroTimer
vArr = Range("A1:B100000").Value2
Dim Matches As Object: Set Matches = CreateObject("Scripting.Dictionary")
arr = Range("G1:G15").Value
For i = 1 To UBound(arr)
For j = 1 To UBound(arr, 2)
If Not Matches.Exists(arr(i, j)) Then Matches.Add arr(i, j), 1
Next j
Next i
For j = LBound(vArr) To UBound(vArr)
If Matches.Exists(vArr(j, 1)) Or Matches.Exists(vArr(j, 2)) Then n = n + 1
Next j
Debug.Print "Using Variant : " & n & " Timer :" & (MicroTimer - dTime) * 1000
End Sub
Dictionary
Sub Test()
Dim rng1 As Range
Set rng1 = Range("A1:B100000")
Dim rng2 As Range
Set rng2 = Range("G1:G15")
Dim i As Long, j As Long
Dim arr As Variant
Dim dTime As Double
dTime = MicroTimer
Dim Matches As Object: Set Matches = CreateObject("Scripting.Dictionary")
arr = rng2.Value
'input the larger data inside a dictionary
For i = 1 To UBound(arr)
For j = 1 To UBound(arr, 2)
If Not Matches.Exists(arr(i, j)) Then Matches.Add arr(i, j), 1
Next j
Next i
'input the shorter data inside an array
arr = rng1.Value
For i = 1 To UBound(arr)
For j = 1 To UBound(arr, 2)
If Matches.Exists(arr(i, j)) Then
'your code if the value is found
cpt = cpt + 1
End If
Next j
Next i
Debug.Print "Using Damian Method : " & cpt & " Timer : " & (MicroTimer - dTime) * 1000
End Sub

Storing a Dynamic Range in Range variable

I am trying to get unique values from dynamic F column and store it in an array. I am getting "Object Required error for my code while setting Selection variable to a dynamic range. Please help.
Sub UniqueFilter()
Dim tmp As String
Dim arr() As String
Dim Selection As Range
Dim lrow As Long
Dim str As String
Dim cell As Range
Dim sht As Worksheet
Set sht = ThisWorkbook.Worksheets("14Feb19")
sht.Activate
'Set Selection = sht.Range(sht.Cells(1, 6), sht.Cells(Rows.Count, 6).End (xlUp)).Select
lrow = shData.Range("F" & Rows.Count).End(xlUp).Row
Set Selection = sht.Range("F2:F" & lrow).Select
If Not Selection Is Nothing Then
For Each cell In Selection
If (cell <> "") And (InStr(tmp, cell) = 0) Then
tmp = tmp & cell & "|"
End If
Next cell
End If
If Len(tmp) > 0 Then tmp = Left(tmp, Len(tmp) - 1)
arr = Split(tmp, "|")
End Sub
You can achieve your goal without having to use Selection at all.
Just copy the range content and transpose it into an array:
Sub UniqueFilter()
Dim arr() As String
Dim tmp As Variant
Dim lrow As Long
Dim sht As Worksheet
Dim index As Integer
Dim count As Integer
Set sht = ThisWorkbook.Worksheets("14Feb19")
sht.Activate
lrow = sht.Range("F" & Rows.count).End(xlUp).Row
'Copying and trasposing selected Range
tmp = Application.Transpose(sht.Range("F2:F" & lrow).Value)
'Cleaning from temp array all empty values
count = 1
For index = 1 To UBound(tmp, 1) - LBound(tmp, 1) + 1
ReDim Preserve arr(1 To count)
If tmp(index) <> "" Then
arr(count) = tmp(index)
count = count + 1
End If
Next
End Sub
(special thanks to #Nathan_Sav, who helped simplifying the code)

Macro to write dictionary keys to array not working

I am trying to use a dictionary to create array of unique items from a column range
The column cells are text (titles)
I know very little about dictionaries, trying to learn something new
I get an array filled with 1's
Thanks
Sub GetUniques()
Dim d As Object, k, a As Variant, c As Variant, i As Long, j As Long, LR As Long
Set d = CreateObject("Scripting.Dictionary")
LR = Cells(Rows.Count, 1).End(xlUp).Row
c = Range("D2:D" & LR).Value2
For i = 1 To UBound(c)
d(c(i, 1)) = 1
Next i
ReDim a(1 To d.Count)
j = 1
For Each k In d.keys
a(j) = k
j = j + 1
Next k
'See what the first item of the array is
MsgBox a(1)
End Sub
I use collection to create unique items. Here is an example
Sub Sample()
Dim ws As Worksheet
Dim lRow As Long, i As Long
Dim Col As New Collection, itm As Variant
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
For i = 1 To lRow
On Error Resume Next
Col.Add .Range("A" & i).Value, CStr(.Range("A" & i).Value)
On Error GoTo 0
Next i
End With
For Each itm In Col
Debug.Print itm
Next
End Sub
EDIT
And if you want to convert that collection to array then you can add this code
Dim MyAr() As Variant
ReDim MyAr(0 To (Col.Count - 1))
For i = 1 To Col.Count
MyAr(i - 1) = Col.Item(i)
Next
Followup from comments
Is this what you are trying?
Sub Sample()
Dim ws As Worksheet
Dim lRow As Long, i As Long
Dim Col As New Collection, itm As Variant
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
For i = 1 To lRow
On Error Resume Next
Col.Add .Range("A" & i).Value, CStr(.Range("A" & i).Value)
On Error GoTo 0
Next i
End With
Dim MyAr() As Variant
ReDim MyAr(0 To (Col.Count - 1))
For i = 1 To Col.Count
MyAr(i - 1) = Col.Item(i)
Next
ws.Range("K1").Resize(UBound(MyAr), 1) = Application.Transpose(MyAr)
End Sub
Note: I see that your query is solved but If I was you, I would use the inbuilt RemoveDuplicates which is much more faster and shorter than the code above
Columns(1).Copy Columns(11)
Columns(11).RemoveDuplicates Columns:=1, Header:=xlNo

Returning Address of Max Cell in Vba

Don't know much about Excel vba. How can I return the location of the cell in the range that is the maximum value (e.g., "MaxVal")?
Sub FillSched()
Dim LTrig As Long
Dim i As Integer
Dim MaxVal As Double
Dim WorkRange As Range
Worksheets("Inputs").Activate
LTrig = Range("Trigger").Value
Worksheets("Daily").Activate
For i = 0 To 5
If Range("AggInvStart").Offset(i, 0).Value > LTrig Then
Set WorkRange = Range("M" & i + 5 & ":" & "O" & i + 5)
MaxVal = WorksheetFunction.Max(WorkRange)
End If
Next i
End Sub
Thank you in advance.
In your code MaxValue is just a calculated value not a range.
You would need to get the position of the WorkRange that contains that MaxValue
Sub FillSched()
Dim LTrig As Long
Dim i As Integer
Dim MaxVal As Double
Dim WorkRange As Range
Dim col Long
Dim rw As Long
Worksheets("Inputs").Activate
LTrig = Range("Trigger").Value
Worksheets("Daily").Activate
For i = 0 To 5
If Range("AggInvStart").Offset(i, 0).Value > LTrig Then
Set WorkRange = Range("M" & i + 5 & ":" & "O" & i + 5)
MaxVal = WorksheetFunction.Max(WorkRange)
rw = WorkRange.Row
col = WorkRange.Column
End If
Next i
MsgBox "MaxValue is in Row: " & rw & ", Column: " & col
End Sub
Sub FillSched()
Dim rngSearch As Range, WorkRange As Range
Dim MaxVal as Double, lCol as Long, lRow as Long, sAddress as String
With WorksheetFunction
Set rngSearch = Range("AggInvStart").Resize(6, 1) ' Define search range
MaxVal = .Max(rngSearch.Value2) ' Get its max value
If MaxVal <= Range("Trigger").Value2 Then _ ' Use this if you do not want to
Exit Sub ' find MaxVal when is <= Range("Trigger")
lCol = Range("AggInvStart")(1,1).Column ' Get first column
lRow = Range("AggInvStart")(1,1).Row - 1 ' Get one row before first
Set WorkRange = Cells(lRow + .Match(MaxVal, rngSearch, 0), lCol) ' Get its location
sAddress = WorkRange.Address ' Get Cell Address
lRow = WorkRange.Row ' Get Row No
lCol = WorkRange.Column ' Get Column No
End With
End Sub
I hope this helps!
PS: I think that in your original code, you want LTrig to be Double. Also, it has to be initialized to a very small number (the code as it stands will fail if the max value is negative because the Dim statement initializes LTrig to zero.)

Resources