Pick random names from different lists excel VBA - excel

I would like to pick random names from columns in excel like this :
-In the first sheet "Inscrp" is where the lists are, and the second sheet "Tirage" is where the results of the picking.
-Column A in the sheet "Tirage" should pick random names from column A in the sheet "Inscrp" and the same for the column B, C , till the number of columns I chose
I managed to do this with only the first column and here is the code :
Sub PickNamesAtRandom()
Dim HowMany As Integer
Dim NoOfNames As Long
Dim RandomNumber As Integer
Dim Names() As String 'Array to store randomly selected names
Dim i As Byte
Dim CellsOut As Long 'Variable to be used when entering names onto worksheet
Dim ArI As Byte 'Variable to increment through array indexes
Application.ScreenUpdating = False
HowMany = 5
CellsOut = 8
ReDim Names(1 To HowMany) 'Set the array size to how many names required
NoOfNames = Application.CountA(Worksheets("Inscrp").Range("A3:A100")) - 1 ' Find how many names in the list
i = 1
Do While i <= HowMany
RandomNo:
RandomNumber = Application.RandBetween(3, NoOfNames + 1)
'Check to see if the name has already been picked
For ArI = LBound(Names) To UBound(Names)
If Names(ArI) = Worksheets("Inscrp").Cells(RandomNumber, 1).Value Then
GoTo RandomNo
End If
Next ArI
Names(i) = Worksheets("Inscrp").Cells(RandomNumber, 1).Value ' Assign random name to the array
i = i + 1
Loop
'Loop through the array and enter names onto the worksheet
For ArI = LBound(Names) To UBound(Names)
Worksheets("Tirage").Cells(CellsOut, 1) = Names(ArI)
CellsOut = CellsOut + 1
Next ArI
Application.ScreenUpdating = True
End Sub

Please, test the next code. If I correctly understand your nee, it will extract HowMany random numbers from each column (nrCol) of "Inscrip" sheet and placed starting from CellsOut in sheet "Tirage". The already extracted name is eliminated from the array where it used to exist (to avoid repeated names). The ranges ar placed in arrays and due to that, the code should be very fast mostly working in memory, even for large ranges:
Sub PickNamesAtRandom()
Dim shI As Worksheet, lastR As Long, shT As Worksheet, HowMany As Long
Dim rndNumber As Integer, Names() As String, i As Long, CellsOut As Long
HowMany = 5: CellsOut = 8
Set shI = Worksheets("Inscrp")
Set shT = Worksheets("Tirage")
Dim col As Long, arrCol, filt As String, nrCol As Long
nrCol = 2 'number of columns to be returned. It can be changed and also be calculated...
For col = 1 To nrCol
lastR = shI.cells(shI.rows.count, col).End(xlUp).Row 'last row in column to be processed
If lastR >= HowMany + 2 Then '+ 2 because the range is build starting with the third row...
arrCol = Application.Transpose(shI.Range(shI.cells(3, col), shI.cells(lastR, col)).Value2) 'place the range in a 1D array
ReDim Names(1 To HowMany) 'Set the array size to how many names required
For i = 1 To UBound(Names)
tryAgain:
Randomize
rndNumber = Int((UBound(arrCol) - LBound(arrCol) + 1) * Rnd + LBound(arrCol))
If arrCol(rndNumber) = "" Then GoTo tryAgain
Names(i) = arrCol(rndNumber)
filt = arrCol(rndNumber) & "##$$#": arrCol(rndNumber) = filt
arrCol = filter(arrCol, filt, False) 'eliminate the already used name from the array
Next i
shT.cells(CellsOut, col).Resize(UBound(Names), 1).Value2 = Application.Transpose(Names)
End If
Next col
MsgBox "Ready..."
End Sub
If something unclear, do not hesitate to ask for clarifications...

Related

VBA Trying to make a function that will randomly assign names to each other without repeating, getting Compile error: Expected array

I am trying to make a button that will take a list of names and match them up with each other over multiple weeks without matching the same people up twice. With the code I have written I get an error that says "Compile Error: Expected Array"
Below is the code that I have so far. Any help would be appreciated
Sub nameMatcher()
Dim column As Integer
Dim cellsDown As Integer
Dim randomNumber As Integer
Dim names As String
Dim i As Byte
Dim arI As Byte
Dim myRange As Range
Dim myCell As Range
Dim numNames As Integer
Worksheets("Sheet1").Activate
cellsDown = 3
column = Application.InputBox(Prompt:="What meeting number is this for?", Type:=1)
numNames = Application.CountA(Range("A:A")) - 1
i = 1
Do While i <= numNames
randomNum:
randomNumber = Application.RandBetween(2, numNames + 1)
For arI = LBound(names) To UBound(names)
If names(arI) = Cells(randomNumber, 1).Value Then
GoTo randomNum
End If
Next arI
names(i) = Cells(randomNumber, 1).Value
i = i + 1
Loop
Worksheet
For arI = LBound(names) To UBound(names)
Cells(cellsDown, column) = names(arI)
cellsDown = cellsDown + 1
Next arI
Application.ScreenUpdating = True
End Sub
Like #Warcupine suggested - your main problem is your not declaring your array as a variant or an array. Both will work, but I think it's simplest to declare as a variant and then assign your column of values to create the array
Change
Dim names As String
To
Dim names As Variant
Then after the line (are you subtracting 1 to ignore header??)
numNames = Application.CountA(Range("A:A")) - 1
Add this line to fill in your array (assuming top row is ignored header)
names = Range("A2:A" & numNames+1)
And then you'll have to change all your array references to use the second dimension as 1
So names(arI) becomes names(arI,1)
and names(i) becomes names(i,1)

Do a loop with multiple constant condition VBA

I am trying to do a loop but I'm a little stuck.
Sub Macro()
Range("A392: A401").Value = Range("N2")
Range("A402: A411").Value = Range("N3")
Range("A412: A421").Value = Range("N4")
Range("A422: A431").Value = Range("N5")
....
I need to repeat this logic ( On column A to set a value for each 10 rows) this value will be from Column N from 1 to 1 until it finds an empty row ...
I'm not being able to do the loop with these multiples conditions, would you please help me ?
Thanks a lot!
Range.Offset is a great method to manipulate ranges. Using it, we can automate the ranges to move down the sheet with each loop.
Sub Macro()
Dim i As Long
While Range("N2").Offset(i) <> ""
'Offset will shift N2 down by one each loop
'Offset will shift the 10 cell range down by 10 on each loop
Range("A392: A401").Offset(i * 10).Value = Range("N2").Offset(i)
i = i + 1
Wend
End Sub
I'm not quite sure what's your actual intention:
Repeat all values in column N2:N5 10 times and append the whole data block to the first free cell in column A.
Repeat all non-empty values in column N 10 times and write them to a fixed target starting with cell A392.
In both cases you can prefill an array and write it to the defined target in column A. Looping through an array has some speed advantages whereas looping through a range by means of VBA can be time consuming.
The direct (untested) copying of whole blocks as shown by #Toddleson can improve this behaviour, depending on total range sizes.
Case 1
Sub Example1()
Const RowsCount As Long = 10
With Sheet1 ' << change to your project's sheet (Code)Name
'get values to repeat & count them
Dim vals: vals = .Range("N2:N5")
Dim cnt As Long: cnt = UBound(vals)
'provide for 1-based 2-dim results array
Dim results
ReDim results(1 To RowsCount * cnt, 1 To 1)
'fill array with repeated values
Dim i As Long, j As Long
For i = 1 To cnt
For j = 1 To RowsCount
results((i - 1) * RowsCount + j, 1) = vals(i, 1)
Next j
Next i
'append above data block
Dim nxtRow As Long
nxtRow = .Range("A" & .Rows.Count).End(xlUp).Row + 1
.Range("A" & nxtRow).Resize(UBound(results), 1) = results
End With
End Sub
Case 2
Sub Example2()
Const RowsCount As Long = 10
With Sheet1 ' << change to your project's sheet (Code)Name
'get values to repeat & count them
Dim lastRow As Long
lastRow = .Range("N" & .Rows.Count).End(xlUp).Row
Dim vals: vals = .Range("N2:N" & lastRow)
Dim ValsCount As Long: ValsCount = UBound(vals)
'provide for 1-based 2-dim results array
Dim results
ReDim results(1 To RowsCount * ValsCount, 1 To 1)
'fill array with repeated values
Dim i As Long, j As Long, ii As Long
For i = 1 To ValsCount
If Len(vals(i, 1)) > 0 Then ' check if non-empty value in column N
ii = ii + 1
For j = 1 To RowsCount
results((ii - 1) * RowsCount + j, 1) = vals(i, 1)
Next j
End If
Next i
'write data block to fixed target starting with A392
.Range("A392").Resize(UBound(results), 1) = results
End With
End Sub

Passing multiple arguments to a function to check duplicate values from an array

I am trying to count duplicate values from a column range using an array, I created a function that takes 3 arguments, number of rows and 2 strings (Long,String,String) for the comparison. I noticed that it only detects duplicates whenever values are next to each other. I already tried looping from 1 to the last row to make sure that each value will be compared to the rest but I got subscript out of range error.
Here's my code:
Sub CountByErrorTest()
Dim rangeArr() As Variant
Dim tester2 As Worksheet
Set tester2 = Worksheets("tester2")
Dim i As Long, j As Long, lrow As Long
lrow = 3169
rangeArr = Worksheets("tester").Range("a2").Resize(lrow, 29).Value2
For i = 1 To 29
For j = 1 To lrow - 1
If dupVal((rangeArr(j, i)), ((rangeArr(j + 1, i)))) Then
tester2.Range("g4") = tester2.Range("g4") + 1
End If
Next j
Next i
End Sub
My function:
Function dupVal(s1 As String, s2 As String) As Boolean
Dim i As Long
If s1 = s2 Then
dupVal = True
Exit Function
End If
dupVal = False
End Function
I tried looping it like this: Gets out of range error
For k = 1 To lrow - 1
If dupVal((rangeArr(j, i)), ((rangeArr(j + k, i)))) Then
tester2.Range("g4") = tester2.Range("g4") + 1
End If
Next k
I would appreciate suggestions to make it better or if there's an easier way to do it that would be really nice.
The duplicates are only being counted when they are next to each other because of this:
dupVal((rangeArr(j, i)), ((rangeArr(j + 1, i))))
The function goes through the entire range and literally compares each column to that column + 1. So, if there was a duplicate 2 or more columns ago, or 1 or more rows ago, the function would not detect it.
The simplest way to detect duplicates is to build a unique list of the values you do know about. Then, when you encounter a new value as you loop through the range, you compare it to that list (which in this case, is an array). If you find it in the list, you count it as a duplicate, because you've seen it before. Otherwise, it's a new value, so add it to the list. In code, this would be:
Function CountByErrorTest() As Long
Const lcol As Long = 29
Const lrow As Long = 3169
Dim uniqueArr() As Variant, find As Variant
Dim tester1 As Worksheet, tester2 As Worksheet
Dim i As Long, j As Long, k As Long, count As Long, uniqueU As Long
Dim found As Boolean
Set tester1 = Worksheets("tester")
Set tester2 = Worksheets("tester2")
uniqueU = -1
count = 0
ReDim uniqueArr(0)
uniqueArr(0) = Null
'Loop through columns
For i = 1 To lcol
'Loop through rows
For j = 1 To lrow
find = tester1.Cells(j, i).Value
found = False
'Loop through array of unique values
For k = LBound(uniqueArr) To UBound(uniqueArr)
'Check whether this is a known value
If (find = uniqueArr(k)) Then
found = True
Exit For
End If
Next k
If found = True Then
'Count the duplicate
count = count + 1
Else
'Add new value to the array
uniqueU = uniqueU + 1
ReDim Preserve uniqueArr(uniqueU)
uniqueArr(uniqueU) = find
End If
Next j
Next i
tester2.Range("g4").Value = count
CountByErrorTest = count
End Function
If you get to a point where you need to do something like this for a very large range, it may start to get slow. To make it more faster, there are data structures which can be used to more efficiently store and retrieve the unique values as you're checking through the range. Using a binary tree instead of an array would be an example of this.
EDIT: Here's an example of how you can generalise this function to accept a range as a parameter and call it from elsewhere:
Function CountByErrorTest(ByRef tester1 As Worksheet, ByRef tester2 As Worksheet, ByRef range As range) As Long
Dim uniqueArr() As Variant, find As Variant
Dim i As Long, j As Long, k As Long, count As Long, uniqueU As Long, lcols As Long, lrows As Long
Dim found As Boolean
lcols = range.Column + range.Columns.count - 1
lrows = range.Row + range.Rows.count - 1
uniqueU = -1
count = 0
ReDim uniqueArr(0)
uniqueArr(0) = Null
'Loop through columns
For i = range.Column To lcols
'Loop through rows
For j = range.Row To lrows
find = tester1.Cells(j, i).Value
found = False
'Loop through array of unique values
For k = LBound(uniqueArr) To UBound(uniqueArr)
'Check whether this is a known value
If (find = uniqueArr(k)) Then
found = True
Exit For
End If
Next k
If found = True Then
'Count the duplicate
count = count + 1
Else
'Add new value to the array
uniqueU = uniqueU + 1
ReDim Preserve uniqueArr(uniqueU)
uniqueArr(uniqueU) = find
End If
Next j
Next i
CountByErrorTest = count
End Function
Sub ExampleCallFunction()
Dim tester1 As Worksheet, tester2 As Worksheet
Dim range As range
Set tester1 = Worksheets("tester")
Set tester2 = Worksheets("tester2")
Set range = tester2.range("b4:c5") 'set what your range is here
tester2.range("g4").Value = CountByErrorTest(tester1, tester2, range)
End Sub

Getting the maximum value of a specific column in a 2d array [duplicate]

I use the code hereunder to calculate max values as described in this post (vba max value of group of values). The code works great but once I have more than 65k lines I get a data type mismatch when trying to pase the array:
sht.Range(Cells(1, lColumn), Cells(last.Row, lColumn)).Value = Application.Index(groupsArray, , lColumn)
Could somebody help me to slice the array in chunks. I have tried to get it working myself but without any luck.
Sub FillGroupsMax()
Dim lColumn As Long
Dim sht As Worksheet
Dim groupsArray As Variant 'array with all group infomation
Dim groupsSeen As Variant 'array with group infomation already seen
Application.ScreenUpdating = False 'stop screen updating makes vba perform better
Set sht = ThisWorkbook.Worksheets("import")
Set last = sht.Range("A:A").Find("*", Cells(1, 1), searchdirection:=xlPrevious) 'last cell with value in column A
lColumn = sht.Cells(1, Columns.Count).End(xlToLeft).Column
groupsArray = sht.Range(Cells(1, 1), Cells(last.Row, lColumn))
'collect all the information on the Sheet into an array
'Improves performance by not visiting the sheet
For dRow = 2 To last.Row 'for each of the rows skipping header
'check if group as already been seen
If inArrayValue(Cells(dRow, 1).Value, groupsSeen) > 0 Then
'if it has been seen/calculated attribute value
'Cells(dRow, 4).Value = inArrayValue(Cells(dRow, 1).Value, groupsSeen)
groupsArray(dRow, lColumn) = inArrayValue(Cells(dRow, 1).Value, groupsSeen)
Else
'if it hasn't been seen then find max
'Cells(dRow, 4).Value = getMax(Cells(dRow, 1).Value, groupsArray)
groupsArray(dRow, lColumn) = getMax(Cells(dRow, 1).Value, groupsArray, lColumn)
'array construction from empty
If IsEmpty(groupsSeen) Then
ReDim groupsSeen(0)
'groupsSeen(0) = Array(Cells(dRow, 1).Value, Cells(dRow, 4).Value)
groupsSeen(0) = Array(groupsArray(dRow, 1), groupsArray(dRow, lColumn))
'attribute value to array
Else
ReDim Preserve groupsSeen(0 To UBound(groupsSeen) + 1)
groupsSeen(UBound(groupsSeen)) = Array(groupsArray(dRow, 1), groupsArray(dRow, lColumn))
End If
End If
Next
sht.Range(Cells(1, lColumn), Cells(last.Row, lColumn)).Value = Application.Index(groupsArray, , lColumn)
'reactivate Screen updating
Application.ScreenUpdating = True
End Sub
Function getMax(group As String, groupsArray As Variant, lColumn As Long) As Double
'for each in array
For n = 1 To UBound(groupsArray)
'if its the same group the Max we seen so far the record
If groupsArray(n, 1) = group And groupsArray(n, lColumn - 1) > maxSoFar Then
maxSoFar = groupsArray(n, lColumn - 1)
End If
Next
'set function value
getMax = maxSoFar
End Function
Function inArrayValue(group As String, groupsSeen As Variant) As Double
'set function value
inArrayValue = 0
'if array is empty then exit
If IsEmpty(groupsSeen) Then Exit Function
'for each in array
For n = 0 To UBound(groupsSeen)
'if we find the group
If groupsSeen(n)(0) = group Then
'set function value to the Max value already seen
inArrayValue = groupsSeen(n)(1)
'exit function earlier
Exit Function
End If
Next
End Function
You can write a helper function to use instead of Application.Index
Bonus - it will be much faster than using Index (>5x)
Sub Tester()
Dim arr, arrCol
arr = Range("A2:J80000").Value
arrCol = GetColumn(arr, 5) '<< get the fifth column
Range("L2").Resize(UBound(arrCol, 1), 1).Value = arrCol
End Sub
'extract a single column from a 1-based 2-D array
Function GetColumn(arr, colNumber)
Dim arrRet, i As Long
ReDim arrRet(1 To UBound(arr, 1), 1 To 1)
For i = 1 To UBound(arr, 1)
arrRet(i, 1) = arr(i, colNumber)
Next i
GetColumn = arrRet
End Function
EDIT - since QHarr asked about timing here's a basic example
Sub Tester()
Dim arr, arrCol, t, i as long
arr = Range("A2:J80000").Value
t = Timer
For i = 1 to 100
arrCol = GetColumn(arr, 5) '<< get the fifth column
Next i
Debug.print Timer - t '<<# of seconds for execution
End Sub
Below, whilst not as tidy as could be, is a way to process an array in chunks and Index to access a column and write out to the sheet.
I populated two columns (A:B) with data. Both had 132,000 rows, populated incrementally, with values from 1 to 132,000 in each column for my test run.
You can fiddle with cutOff to get the chunk size just below the point where the fail happens.
The code below is simply to demonstrate the principle of looping in batches, upto the set cutoff in each batch, until all rows have been processed.
Option Explicit
Public Sub WriteArrayToSheet()
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ThisWorkbook
Set sht = wb.Worksheets("Sheet1") 'change as appropriate
Dim myArr() 'dynamic array
myArr = sht.Range("A1").CurrentRegion.Value 'you may want a more robust method
Dim cutOff As Long 'the max value - what ever it is before error occurs
cutOff = 1000
Dim totalRows As Long 'total rows in array read in from sheet
totalRows = UBound(myArr, 1)
Dim totalArraysNeeded As Long
'Determine how many lots of cutOff chunks there are in the total number of array rows
totalArraysNeeded = Application.WorksheetFunction.Ceiling(totalRows / cutOff, 1)
Dim rotations As Long 'number of times to loop original array to handle all rows
Dim rowCountTotal As Long
Dim rowCount As Long
Dim tempArr() 'this will hold the chunk of the original array
Dim rowCounter As Long
Dim lastRow As Long
Dim nextRow As Long
Dim i As Long
Dim j As Long
Dim numRows As Long
rotations = 1
Do While rotations < totalArraysNeeded
If rotations < totalArraysNeeded - 1 Then
ReDim tempArr(1 To cutOff, 1 To UBound(myArr, 2)) 'size chunk array
numRows = cutOff
Else
numRows = totalRows - rowCountTotal
ReDim tempArr(1 To numRows, 1 To UBound(myArr, 2)) 'size chunk array
End If
For i = 1 To numRows
rowCount = 1 'rows in this chunk looped
rowCountTotal = rowCountTotal + 1 'rows in original array looped
For j = LBound(myArr, 2) To UBound(myArr, 2)
tempArr(i, j) = myArr(rowCountTotal, j)
Next j
rowCount = rowCount + 1
Next i
With sht
lastRow = .Cells(.Rows.Count, "E").End(xlUp).Row 'Column where I am writing the sliced column out to
End With
If lastRow = 1 Then
nextRow = 1
Else
nextRow = lastRow + 1
End If
sht.Range("E" & nextRow).Resize(UBound(tempArr, 1), 1) = Application.Index(tempArr, , 1) 'write out to sheet
rotations = rotations + 1
Loop
End Sub
As #Tim suggested, the best way to slice a large array is use a loop to copy the column.
Though in your case, most of the processing time is spent on computing the maximum since your code is using a nested loop.
If you want to reduce significantly the processing time, then use a dictionary:
Sub Usage
GetMaxByGroupTo _
sourceGroups := ThisWorkbook.Range("Sheet1!A2:A100"), _
sourceValues := ThisWorkbook.Range("Sheet1!B2:B100"), _
target := ThisWorkbook.Range("Sheet1!C2")
End Sub
Sub GetMaxByGroupTo(sourceGroups As Range, sourceValues As Range, target As Range)
Dim dict As Object, groups(), values(), r As Long, max
Set dict = CreateObject("Scripting.Dictionary")
groups = sourceGroups.Value2
values = sourceValues.Value2
' store the maximum value of each group in a dictionary for an efficient lookup '
For r = Lbound(groups) to Ubound(groups)
max = dict(groups(r, 1))
If VarType(max) And values(r, 1) <= max Then Else dict(groups(r, 1)) = values(r, 1)
Next
' build and copy the result array to the sheet '
For r = Lbound(groups) to Ubound(groups)
values(r, 1) = dict(groups(r, 1))
Next
target.Resize(Ubound(groups), 1).Value2 = values
End Sub

Compare two sheets and highlight unmatched rows using unique ID only

I want to match rows from two different sheets and highlight only in the first column of the unmatched row or better still copy the unmatched rows into a new sheet. The code should compare the rows of the two Sheets and color the new rows in the second sheet. Sheet2 (say Jan 2020) contains more rows than Sheet1 (Dec 2019) as its the recently updated sheet and they both contain rows of over 22k with both having unique ID as the first column.
My below code tries to highlight all the unmatching cells and takes longer time to finish. What I wish is for the code to just color the unmatched in column A (the vb.Red) only(since its the unique ID) while ignoring the rest of the column/cells (vb.Yellow) and or if possible copy the highlighted rows into a new sheet.
Sub RunCompare()
Call compareSheets("Sheet1", "Sheet2") 'compareSheets("2019-01 Database", "2019-02 Database")
End Sub
Sub compareSheets(shtSheet1 As String, shtSheet2 As String)
Dim c As Integer, j As Integer, i As Integer, mydiffs As Integer, cnt1 As Integer, cnt2 As Integer
Dim noexist As Integer
cnt2 = Worksheets("Sheet2").Cells.SpecialCells(xlCellTypeLastCell).Row
cnt1 = Worksheets("Sheet1").Cells.SpecialCells(xlCellTypeLastCell).Row
'For each cell in sheet2 that is not the same in Sheet1, color it yellow
For i = 1 To cnt2
For j = 1 To cnt1
If ActiveWorkbook.Worksheets(shtSheet2).Cells(i, 1).Value = ActiveWorkbook.Worksheets(shtSheet1).Cells(j, 1).Value Then
For c = 2 To 22
If Not ActiveWorkbook.Worksheets(shtSheet2).Cells(i, c).Value = ActiveWorkbook.Worksheets(shtSheet1).Cells(j, c).Value Then
ActiveWorkbook.Worksheets(shtSheet2).Cells(i, c).Interior.Color = vbYellow
mydiffs = mydiffs + 1
End If
Next
Exit For
End If
If j = cnt1 Then
ActiveWorkbook.Worksheets(shtSheet2).Cells(i, 1).Interior.Color = vbRed
End If
Next
Next
'Display a message box to demonstrate the differences and if there is a new entry on the second sheet
'MsgBox mydiffs & ":differences found, " & noexist & ":no exist", vbInformation
ActiveWorkbook.Sheets(shtSheet2).Select
End Sub
Let's simplify the task and do it step by step.
This is how the input in the two sheets can look like:
Then, we may consider reading these and saving them to an array:
Set rangeA = ThisWorkbook.Worksheets(1).Range("A1:Z1")
Set rangeB = ThisWorkbook.Worksheets(2).Range("A1:ZZ1")
Dim arrayA As Variant
Dim arrayB As Variant
With Application
arrayA = .Transpose(.Transpose(rangeA))
arrayB = .Transpose(.Transpose(rangeB))
End With
Looping between the data in the two arrays is quite fast in vba. The writing to the third worksheet is done only once the two values from the two arrays match:
Dim myValA As Variant
Dim myValB As Variant
Dim currentRow As Long: currentRow = 1
For Each myValA In arrayA
For Each myValB In arrayB
If myValA = myValB Then
ThisWorkbook.Worksheets(3).Cells(currentRow, 1) = myValA
currentRow = currentRow + 1
End If
Next
Next
This is the result in the third worksheet, all matching values are in a single row:
This is how the whole code looks like:
Sub CompareTwoRanges()
Dim rangeA As Range
Dim rangeB As Range
Set rangeA = ThisWorkbook.Worksheets(1).Range("A1:Z1")
Set rangeB = ThisWorkbook.Worksheets(2).Range("A1:ZZ1")
Dim arrayA As Variant
Dim arrayB As Variant
With Application
arrayA = .Transpose(.Transpose(rangeA))
arrayB = .Transpose(.Transpose(rangeB))
End With
Dim myValA As Variant
Dim myValB As Variant
Dim currentRow As Long: currentRow = 1
For Each myValA In arrayA
For Each myValB In arrayB
If myValA = myValB Then
ThisWorkbook.Worksheets(3).Cells(currentRow, 1) = myValA
currentRow = currentRow + 1
End If
Next
Next
End Sub
Note - there will be another performance bonus, if the results are written to an array and then written from the array to the worksheet. Thus the writing would happen only once. This is the change, that needs to be implemented in the code, after the array declarations:
Dim myValA As Variant
Dim myValB As Variant
Dim resultArray() As Variant
ReDim Preserve resultArray(2 ^ 20)
Dim i As Long: i = 0
For Each myValA In arrayA
For Each myValB In arrayB
If myValA = myValB Then
resultArray(i) = myValA
i = i + 1
End If
Next
Next
ReDim Preserve resultArray(i)
ThisWorkbook.Worksheets(3).Cells(1, 1).Resize(UBound(resultArray)) = Application.Transpose(resultArray)
when you get cell value, it spends time.
so, you can target Range transfer 2d Variant
Dim endRow AS Long
Dim olderRange AS Range
Dim olderVariant AS Variant
endRow = olderSheet.cells(rows.count,1).end(xlup).row
Set olderRange = olderSheet.Range(olderSheet.Cells(startRow, startCol), olderSheet.Cells(endRow, endCol))
'Transfer
olderVariant = olderRange
For currentRow = 1 to UBound(olderVariant, 1)
'Loop
'if you want change real Cell value Or interior
'add row Or Col weight
if olderVariant(currentRow, currentCol) = newerVariant(currentRow, currentCol) THen
newerSheet.Cells(currentRow+10,currentCol+10).interior.colorIndex = 3
End if
Next currentRow
In case anyone has the same kind of problem, I have found an easier way to do it. Providing your sheet2 is the comparison sheet:
Dim Ary1 As Variant, Ary2 As Variant
Dim r As Long
Ary1 = Sheets("Sheet1").UsedRange.Value2
Ary2 = Sheets("Sheet2").UsedRange.Value2
With CreateObject("scripting.dictionary")
For r = 1 To UBound(Ary1)
.Item(Ary1(r, 1)) = Empty
Next r
For r = 1 To UBound(Ary2)
If Not .Exists(Ary2(r, 1)) Then Sheets("Sheet2").Cells(r, 1).Interior.Color = vbRed
Next r
End With

Resources