VBA - Set Range from Max in range 2 - vba

I edited my orignal post, it seems to be working for the most part, but only for the contract, and subsequent contracts it pulls the second to last number, not the last number. Also it will not work for one line contracts, ie. 1 year. This works only for the first contract.
The subsequent contracts are differentiated by the Column A. Where a new contract number begins. The goal is to have the last value from Column I for each contract. For example, the contract that is the area A11:L15, the value in J11 should equal the value in I15. And this should be true for later contracts including contracts that are only one year like A126 in the second image
.
If someone has any suggestions, it would be much appreciated.
Dim lngLastRow As Long, rngCell As Range, rngRange As Range, _
lngMin As Long, lngMax As Long, lngPreviousRow As Long, _
raw As Worksheet, data As Worksheet, dLRow As Double, endDate As Double, _
r As Range, n As Long
lngLastRow = lastRow(column_to_check:=2)
Set raw = Worksheets("Raw")
Set data = Worksheets("Data")
Set rngRange = raw.Range(raw.Cells(2, 1), raw.Cells(lngLastRow + 1, 1))
dLRow = data.Range("A1", data.Range("A1").End(xlDown)).Rows.Count
raw.Range("J:J").EntireColumn.Insert
raw.Range("C:E").EntireColumn.NumberFormat = "mm/dd/yyyy"
For Each rngCell In rngRange
If Len(rngCell) > 0 Then
If lngPreviousRow > 0 And (rngCell.Row - 1 <> lngPreviousRow) Then
raw.Cells(lngPreviousRow, 10) = s.Cells(n).Offset(0, 6)
End If
If (rngCell.Row = 1) Or lngPreviousRow = (rngCell.Row - 1) Then
Set r = raw.Range(rngCell.Offset(0, 1), rngCell(0, 2))
Set s = raw.Range(rngCell.Offset(0, 2), rngCell(0, 3))
lngMin = WorksheetFunction.Min(r)
lngMax = WorksheetFunction.Max(s)
m = Application.Match(lngMin, r, 0)
n = Application.Match(lngMax, s, 0)
raw.Cells(rngCell.Row, 10) = s.Cells(n).Offset(0, 6)
End If
lngPreviousRow = rngCell.Row
Set r = raw.Range(rngCell.Offset(0, 1), rngCell(0, 2))
Set s = raw.Range(rngCell.Offset(0, 2), rngCell(0, 3))
lngMin = WorksheetFunction.Min(r)
lngMax = WorksheetFunction.Max(s)
m = Application.Match(lngMin, r, 0)
n = Application.Match(lngMax, s, 0)
Else
Set r = raw.Range(rngCell.Offset(0, 1), rngCell(0, 2))
Set s = raw.Range(rngCell.Offset(0, 2), rngCell(0, 3))
lngMin = WorksheetFunction.Min(r)
lngMax = WorksheetFunction.Max(s)
End If
Next rngCell
Cells(lngPreviousRow, 10) = s.Cells(n).Offset(0, 6)

From what I understand, you want the first row of the contract to show the last contract value. Additionally, it appears that the contract description (column K) is consistent for a given contract. If I understand your question correctly, simply loop through the description to look for changes. Then input the value into the first unique cell corresponding to the given description.
Dim Rng As Range
Set Rng = Range("k2:k146")
Dim NextCell As Range
For Each Cell In Rng
Set NextCell = Cell
Do Until NextCell.Text <> Cell.Text
Set NextCell = NextCell.Offset(1, 0)
Loop
Set NextCell = NextCell.Offset(-1, 0)
If Cell.Offset(-1, 0).Text <> Cell.Text Then
Cell.Offset(0, -1).Value = NextCell.Offset(0, -2).Value
End If
Next Cell

I was able to solve it. Thanks to #E.Merckx for helping point me in the right direction. Although it wasn't exactly what I wanted, it works just fine for its purpose.
Sub NetValue()
Dim lngLastRow As Long, raw As Worksheet, data As Worksheet, rng As Range
lngLastRow = lastRow(column_to_check:=2)
Set raw = Worksheets("Raw")
Set data = Worksheets("Data")
Set rng = raw.Range(raw.Cells(3, 6), raw.Cells(lngLastRow + 1, 6))
raw.Range("J:J").EntireColumn.Insert
raw.Range("C:E").EntireColumn.NumberFormat = "mm/dd/yyyy"
For Each Cell In rng
If Cell.Value <> "" Then
Cell.Offset(-1, 4) = Cell.Offset(-1, 3).Value
End If
Next Cell
End Sub
Thanks again!

Related

Excel VBA Dictionary: add matching criteria if the data doesn't match with dictionary

I have been working on finding a way to add a matching criteria to another workbook almost this day, but I did not find anyway to do it yet. The example scenario is
the following, I have two workbooks (workbookA and workbookB) and each workbook has their own "Country" and "Value" lists. Kindly see sample tables per below.
Workbook("WorkA").Sheet1 Workbook("workB").Sheet1
Country Value Country Value
A 10 B
B 15 D
C 20 E
D 25 A
E 30
F 35
I finished matching value column by the following code:
Sub Test_match_fill_data()
Dim Dict As Object
Dim key As Variant
Dim aCell, bCell As Range
Dim i, j As Long
Dim w1, w2 As Worksheet
Set Dict = CreateObject("Scripting.Dictionary")
Set w1 = Workbooks("workA").Sheets("Sheet1")
Set w2 = Workbooks("workB").Sheets("Sheet1")
i = w1.Cells(w1.Rows.Count, 1).End(xlUp).row
For Each aCell In w1.Range("A6:A" & i)
If Not Dict.exists(aCell.Value) Then
Dict.Add aCell.Value, aCell.Offset(0, 2).Value
End If
Next
j = w2.Cells(w2.Rows.Count, 1).End(xlUp).row
For Each bCell In w2.Range("A6:A" & j)
For Each key In Dict
If bCell.Value = key Then
bCell.Offset(0, 2).Value = Dict(key)
End If
Next
Next
End Sub
What I would like to do is to add some missing countries from "workA" (in this case are countries "C" and "F") and then redo matching process again to gathered all of data. Copy and paste solution is not suit to my case since I have to gather time series data (trade data) and it is possibly that some months my interested country will trade with new partners. I have tried to research on this in several websites and been deep down and adjusted my code with other people's codes as following link:
Dictionary add if doesn't exist, Looping Through EXCEL VBA Dictionary, Optimise compare and match method using scripting.dictionary in VBA, A 'flexible' VBA approach to lookups using arrays, scripting dictionary
Can any potential gurus suggest me the solutions or ideas to deal with this kind of problems? It would be nice if you could explain your reasoning behind the code or any mistake I had made.
Thank you!
With minimal changes to your code:
Sub Test_match_fill_data()
Dim Dict As Object
Dim key As Variant
Dim aCell As Range, bCell As Range
Dim i As Long, j As Long
Dim w1 As Worksheet, w2 As Worksheet
Set Dict = CreateObject("Scripting.Dictionary")
Set w1 = Workbooks("workA").Sheets("Sheet1")
Set w2 = Workbooks("workB").Sheets("Sheet1")
i = w1.Cells(w1.Rows.Count, 1).End(xlUp).row
For Each aCell In w1.Range("A6:A" & i)
Dict(aCell.Value) = aCell.Offset(0, 2).Value
Next
j = w2.Cells(w2.Rows.Count, 1).End(xlUp).row
For Each bCell In w2.Range("A6:A" & j)
If Dict.Exists(bCell.Value) Then
bCell.Offset(0, 2).Value = Dict(bCell.Value)
Dict.Remove bCell.Value
End If
Next
For Each key In Dict
With w2.Cells(w2.Rows.Count, 1).End(xlUp).Offset(1)
.Value = key
.Offset(,2) = Dict(key)
End With
Next
End Sub
while a slightly more condensed version of it could be the following:
Sub Test_match_fill_data()
Dim Dict As Object
Dim key As Variant
Dim cell As Range
Set Dict = CreateObject("Scripting.Dictionary")
With Workbooks("workA").Sheets("Sheet1")
For Each cell In .Range("A6", .Cells(.Rows.count, 1).End(xlUp))
Dict(cell.Value) = cell.Offset(0, 2).Value
Next
End With
With Workbooks("workB").Sheets("Sheet1")
For Each cell In .Range("A6", .Cells(Rows.count, 1).End(xlUp))
If Dict.Exists(cell.Value) Then
cell.Offset(0, 2).Value = Dict(cell.Value)
Dict.Remove cell.Value
End If
Next
For Each key In Dict
With .Cells(.Rows.count, 1).End(xlUp).Offset(1)
.Value = key
.Offset(, 2) = Dict(key)
End With
Next
End With
End Sub
for a "Fast&Furious" code you want massive use of array and dictionaries and limit excel sheet range accesses to the minimum
so the following code is obtained from my last one, but limiting excel sheets range accesses to initial data reading and final data writing, both in "one shot" mode (or nearly)
Sub Test_match_fill_data()
Dim Dict As Object
Dim iItem As Long
Dim workACountries As Variant, workAValues As Variant
Dim workBCountries As Variant, workBValues As Variant
With Workbooks("workA").Sheets("Sheet1")
workACountries = .Range("A6", .Cells(.Rows.count, 1).End(xlUp)).Value
workAValues = .Range("C6:C" & .Cells(.Rows.count, 1).End(xlUp).Row).Value
End With
Set Dict = CreateObject("Scripting.Dictionary")
For iItem = 1 To UBound(workACountries)
Dict(workACountries(iItem, 1)) = workAValues(iItem, 1)
Next
With Workbooks("workB").Sheets("Sheet1")
workBCountries = .Range("A6", .Cells(.Rows.count, 1).End(xlUp)).Value
workBValues = .Range("C6:C" & .Cells(.Rows.count, 1).End(xlUp).Row).Value
End With
For iItem = 1 To UBound(workBCountries)
If Dict.Exists(workBCountries(iItem, 1)) Then
workBValues(iItem, 1) = Dict(workBCountries(iItem, 1))
Dict.Remove workBCountries(iItem, 1)
End If
Next
With Workbooks("workB").Sheets("Sheet1")
.Range("A6").Resize(UBound(workBCountries)).Value = workBCountries
.Range("C6").Resize(UBound(workBCountries)).Value = workBValues
.Cells(.Rows.count, 1).End(xlUp).Offset(1).Resize(Dict.count).Value = Application.Transpose(Dict.Keys)
.Cells(.Rows.count, 3).End(xlUp).Offset(1).Resize(Dict.count).Value = Application.Transpose(Dict.Items)
End With
End Sub
I don't think you need to use a dictionary for this - you can just go through every value in Book1, column A, check if it exists in the range in Book2 column A, and if it does, you can port over its corresponding value - if it DOESN'T, add it to the end and bring over its associated value. This is a simple, dynamic solution.
Note the simple use of .Find to return the row position:
Sub Test_match_fill_data()
Dim aCell
Dim i, j As Long, keyrow As Long
Dim w1, w2 As Worksheet
Set w1 = Workbooks("Book1").Sheets("Sheet1")
Set w2 = Workbooks("Book2").Sheets("Sheet1")
i = w1.Cells(w1.Rows.Count, 1).End(xlUp).Row
j = w2.Cells(w2.Rows.Count, 1).End(xlUp).Row
For Each aCell In w1.Range("A2:A" & i)
On Error Resume Next
keyrow = w2.Columns("A:A").Find(What:=aCell, LookAt:=xlWhole).Row
On Error GoTo 0
If keyrow = 0 Then
w2.Range("A" & j + 1).Value = aCell
w2.Range("B" & j + 1).Value = aCell.Offset(0, 1).Value
j = j + 1
Else
w2.Range("B" & keyrow).Value = aCell.Offset(0, 1).Value
End If
keyrow = 0
Next
End Sub

VBA refining range

I am attempting to draw data from a separate sheet and put it into a corresponding cell if the conditions are met. My code works, but it is not efficient. I do not know how to change the For Next loop so that it attempts to draw data only until the final entry. Right now I have it set to go a hundred or so cells further than I need so that I wouldn't have to update the code as often when I input new data to the data sheet (or at least that was the thought). Here is my code:
Sub LRearTest()
Dim R As Integer
Dim j As Integer
For j = 89 To 250
For R = 1 To 300
If Worksheets("Input").Cells(j, 22).Value >= Worksheets("1036L").Cells(R, 5).Value And Worksheets("Input").Cells(j, 22).Value <= Worksheets("1036L").Cells(R, 6).Value Then
Worksheets("Input").Cells(j, 20).Value = Worksheets("1036L").Cells(R, 3).Value
End If
Next R
Next j
End Sub
The problem is when I run this code it takes almost two minutes before it is over. I am not sure if it is because I have used j and r as integers or what. Also I have a dozen of these on one module so I am not sure if that contributes. The code works like I said, it is just far too slow. Help is greatly appreciated.
The point that I am checking is in Column V of Sheet "Input". Each of my columns that I want to populate, F - U, use the same data in column V. The sheets that I am comparing the data in column V against are labeled as 1030L, 1030R, 1031L, 1031R, 1032L, 1032R, 1033L, 1033R, 1034L, 1034R, 1034LA, 1034RA, 1035L, 1035R, 1036L, and 1036R. The data being compared is in the same columns in every sheet. Thank you
Something like this should work for you:
Sub LRearTest()
Dim wb As Workbook
Dim wsInput As Worksheet
Dim wsData As Worksheet
Dim aDataParams() As String
Dim aInput As Variant
Dim aData As Variant
Dim InputIndex As Long
Dim DataIndex As Long
Dim ParamIndex As Long
Dim MinCol As Long
Set wb = ActiveWorkbook
Set wsInput = wb.Sheets("Input")
'Adjust the column associations for each sheet as necessary
ReDim aDataParams(1 To 16, 1 To 3)
aDataParams(1, 1) = "1030L": aDataParams(1, 2) = "F"
aDataParams(2, 1) = "1030R": aDataParams(2, 2) = "G"
aDataParams(3, 1) = "1031L": aDataParams(3, 2) = "H"
aDataParams(4, 1) = "1031R": aDataParams(4, 2) = "I"
aDataParams(5, 1) = "1032L": aDataParams(5, 2) = "J"
aDataParams(6, 1) = "1032R": aDataParams(6, 2) = "K"
aDataParams(7, 1) = "1033L": aDataParams(7, 2) = "L"
aDataParams(8, 1) = "1033R": aDataParams(8, 2) = "M"
aDataParams(9, 1) = "1034L": aDataParams(9, 2) = "N"
aDataParams(10, 1) = "1034R": aDataParams(10, 2) = "O"
aDataParams(11, 1) = "1034LA": aDataParams(11, 2) = "P"
aDataParams(12, 1) = "1034RA": aDataParams(12, 2) = "Q"
aDataParams(13, 1) = "1035L": aDataParams(13, 2) = "R"
aDataParams(14, 1) = "1035R": aDataParams(14, 2) = "S"
aDataParams(15, 1) = "1036L": aDataParams(15, 2) = "T"
aDataParams(16, 1) = "1036R": aDataParams(16, 2) = "U"
'Find minimum column
MinCol = wsInput.Columns.Count
For ParamIndex = LBound(aDataParams, 1) To UBound(aDataParams, 1)
If wsInput.Columns(aDataParams(ParamIndex, 2)).Column < MinCol Then MinCol = wsInput.Columns(aDataParams(ParamIndex, 2)).Column
Next ParamIndex
'Based on minimum column, determine column indexes for each sheet/column pair
For ParamIndex = LBound(aDataParams, 1) To UBound(aDataParams, 1)
aDataParams(ParamIndex, 3) = wsInput.Columns(aDataParams(ParamIndex, 2)).Column - MinCol + 1
Next ParamIndex
With wsInput.Range("F89", wsInput.Cells(wsInput.Rows.Count, "V").End(xlUp))
If .Row < 89 Then
MsgBox "No data in sheet [" & wsInput.Name & "]"
Exit Sub
End If
aInput = .Value
End With
For ParamIndex = LBound(aDataParams, 1) To UBound(aDataParams, 1)
'Define data sheet based on current column
Set wsData = wb.Sheets(aDataParams(ParamIndex, 1))
aData = wsData.Range("C1", wsData.Cells(wsData.Rows.Count, "F").End(xlUp)).Value
For InputIndex = LBound(aInput, 1) To UBound(aInput, 1)
For DataIndex = LBound(aData, 1) To UBound(aData, 1)
If aInput(InputIndex, UBound(aInput, 2)) >= aData(DataIndex, 3) _
And aInput(InputIndex, UBound(aInput, 2)) <= aData(DataIndex, 4) Then
aInput(InputIndex, aDataParams(ParamIndex, 3)) = aData(DataIndex, 1)
Exit For
End If
Next DataIndex
Next InputIndex
Set wsData = Nothing
Erase aData
Next ParamIndex
wsInput.Range("F89").Resize(UBound(aInput, 1), UBound(aInput, 2)) = aInput
Set wb = Nothing
Set wsInput = Nothing
Set wsData = Nothing
Erase aInput
Erase aData
Erase aDataParams
End Sub

Compare 2 sets of data and paste any missing values on another sheet

So I have a master sheet with 1000+ rows and another sheet that "should" have the same data. however, in reality sometimes some is missing from the master and sometimes some is missing from the query run.
for simplicity purposes let's say the unique ID is in column B. here's my code but it's super slow and it only does a 1-way comparison.
My ideal code would be something that runs a little smoother and gives me the missing data from both the master and the query.
Is there's something wrong with the way I'm asking the question please let me know.
Sub FindMissing()
Dim lastRowE As Integer
Dim lastRowF As Integer
Dim lastRowM As Integer
Dim foundTrue As Boolean
lastRowE = Sheets("Master").Cells(Sheets("Master").Rows.Count, "B").End(xlUp).Row
lastRowF = Sheets("Qry").Cells(Sheets("Qry").Rows.Count, "B").End(xlUp).Row
lastRowM = Sheets("Mismatch").Cells(Sheets("Mismatch").Rows.Count, "B").End(xlUp).Row
For i = 1 To lastRowE
foundTrue = False
For j = 1 To lastRowF
If Sheets("Master").Cells(i, 2).Value = Sheets("Qry").Cells(j, 2).Value Then
foundTrue = True
Exit For
End If
Next j
If Not foundTrue Then
Sheets("Master").Rows(i).Copy Destination:= _
Sheets("Mismatch").Rows(lastRowM + 1)
lastRowM = lastRowM + 1
End If
Next i
End Sub
Don't loop through the cells on the worksheet. Collect all of the values into variant arrays and process in-memory.
Option Explicit
Sub YouSuckAtVBA()
Dim i As Long, mm As Long
Dim valsM As Variant, valsQ As Variant, valsMM As Variant
With Worksheets("Master")
valsM = .Range(.Cells(1, "B"), .Cells(.Rows.Count, "B").End(xlUp)).Value2
End With
With Worksheets("Qry")
valsQ = .Range(.Cells(1, "B"), .Cells(.Rows.Count, "B").End(xlUp)).Value2
End With
ReDim valsMM(1 To (UBound(valsM, 1) + UBound(valsQ, 1)), 1 To 2)
mm = 1
valsMM(mm, 1) = "value"
valsMM(mm, 2) = "missing from"
For i = LBound(valsM, 1) To UBound(valsM, 1)
If IsError(Application.Match(valsM(i, 1), valsQ, 0)) Then
mm = mm + 1
valsMM(mm, 1) = valsM(i, 1)
valsMM(mm, 2) = "qry"
End If
Next i
For i = LBound(valsQ, 1) To UBound(valsQ, 1)
If IsError(Application.Match(valsQ(i, 1), valsM, 0)) Then
mm = mm + 1
valsMM(mm, 1) = valsQ(i, 1)
valsMM(mm, 2) = "master"
End If
Next i
valsMM = helperResizeArray(valsMM, mm)
With Worksheets("Mismatch")
With .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0)
.Resize(UBound(valsMM, 1), UBound(valsMM, 2)) = valsMM
End With
End With
End Sub
Function helperResizeArray(vals As Variant, x As Long)
Dim arr As Variant, i As Long
ReDim arr(1 To x, 1 To 2)
For i = LBound(arr, 1) To UBound(arr, 1)
arr(i, 1) = vals(i, 1)
arr(i, 2) = vals(i, 2)
Next i
helperResizeArray = arr
End Function
You cannot resize the first rank of a 2D array so I've added a helper function that will resize the results before putting them back into the Mismatch worksheet.

VBA define beginning and ending of a Macro (strings) for each section?

Hello , how can I define an Interval based on strings and that loops into it ?
for example to define from 1 start -- to 2 end, and then move to the other interval ?
This is to search for a name "xx" for example in each interval and show with msgbox the adress of cell .
I did the following code , the problem is that I don't know how to make that interval .. can someone help me please ? Thanks.
Sub search_for_names()
lastligne = ThisWorkbook.Sheets("students").Cells(Rows.Count, 1).End(xlUp).Row
For i = 1 To lastligne
Set rnginformation = Cells(i, 1)
Set rngaction = Cells(i, 2)
If rnginformation = "1" And rngaction = "start" Then
MsgBox "beginning of interval"
For k = 1 To rnginformation = "2" And rngaction = "end" 'define the end of interval
MsgBox "x"
Set actionAnalyse = ThisWorkbook.Sheets("students").Cells(k, 2).Find(xx, LookIn:=xlValues)
firstAddress = rngaction.Address
MsgBox firstAddress
Next k
End If
Next i
End Sub
When you find your starting row set a variable equal to i lStart = i. Then when you find the last row build your range from the starting row to i .Range(.Cells(lStart, 2), .Cells(i, 2)). Since you actually want the rows in between lStart and i .Range(.Cells(lStart + 1, 2), .Cells(i -1, 2)) would be more efficient.
Sub search_for_names()
Dim i As Long, lastligne As Long, lStart As Long
Dim actionAnalyse As Range, rSearch As Range
With Sheets("students")
lastligne = .Cells(Rows.Count, 1).End(xlUp).Row
For i = 1 To lastligne
If .Cells(i, 1) = "1" And .Cells(i, 2) = "start" Then lStart = i
If .Cells(i, 1) = "2" And .Cells(i, 2) = "end" Then
Set rSearch = .Range(.Cells(lStart, 2), .Cells(i, 2))
Set actionAnalyse = rSearch.Find("xx", LookIn:=xlValues)
If Not actionAnalyse Is Nothing Then
MsgBox actionAnalyse.Address
End If
End If
Next i
End With
End Sub

Runtime 13 Type mismatch

I get run time error 13 when executing following code
Dim sh, shmem As Worksheet
Dim rw As Range
Set shmem = Sheets("SHEET1")
Set sh = Sheets("SHEET2")
For Each rw In sh.Rows
If sh.Cells(rw.Row, 1).Value = "" And sh.Cells(rw.Row, 2).Value = "" Then
Exit For
End If
With Application.WorksheetFunction
Dim bdaytest As Variant
Dim match1 As Double
bdaytest = .Index((shmem.Range("A2:A121") = sh.Cells(rw.Row, 1)) * (shmem.Range("A2:A121") = sh.Cells(rw.Row, 1)), 0)
'match1 = .Match(1, .Index((shmem.Range("A2:A121") = sh.Cells(rw.Row, 1)) * (shmem.Range("A2:A121") = sh.Cells(rw.Row, 1)), 0), 0)
bdaytest = .Index(1, shmem.Range("D2:D121"), match1)
End With
Next rw
The Error Happens in following line which I extracted from the 2 line (commented out now)
bdaytest = .Index((shmem.Range("A2:A121") = sh.Cells(rw.Row, 1)) * (shmem.Range("A2:A121") = sh.Cells(rw.Row, 1)), 0)
'match1 = .Match(1, .Index((shmem.Range("A2:A121") = sh.Cells(rw.Row, 1)) * (shmem.Range("A2:A121") = sh.Cells(rw.Row, 1)), 0), 0)
I understand that the error must happen because bdaytest is the wrong data type but I'm not sure and up to now I couldn't find any solution. Thanks in advance for any suggestions.
Edit: I want to find out the Row Number of the Line where 2 Columns (A & B) have a requested Value. The requested Value is found in sh.Cells(rw.Row, 1) and sh.Cells(rw.Row, 2)
You can't create arrays using = and * like that in VBA, unlike in a formula. What you can do is use Application.Countifs like this:
Dim sh As Worksheet
Dim shmem As Worksheet
Dim rw As Range
Set shmem = Sheets("SHEET1")
Set sh = Sheets("SHEET2")
For Each rw In sh.Rows
If sh.Cells(rw.Row, 1).Value = "" And sh.Cells(rw.Row, 2).Value = "" Then
Exit For
End If
With Application
Dim bdaytest As Variant
Dim match1 As Double
bdaytest = .Match(1, .CountIfs(sh.Cells(rw.Row, 1), shmem.Range("A2:A121"), _
sh.Cells(rw.Row, 2), shmem.Range("B2:B121")), 0)
If Not IsError(bdaytest) Then bdaytest = shmem.Range("D2:D121").Cells(bdaytest)
End With
Next rw
Note: WorksheetFunction.Countifs will not work.