Assign all rows and both columns of data to an array - vba

I'm trying to return all rows of data of an unknown amount [8 rows] from two columns. I get the subscript out of range error for >> SERnumber(rws, clm).
I'm simply trying to return:
in AAA
out AAA
in AAA
in VVV
in GGG
Here's my non-working code:
Sub Button1_Click()
Dim SERnumber() As String
Dim i As Integer
Dim strMessage As String
Dim rws As Integer
Dim clm As Integer
' assign variable > rws the # of rows containing data
rws = Cells(Rows.Count, 2).End(xlUp).Row
' Redimension the SERnumber array variable >>
' for n? rows and 2 columns
ReDim SERnumber(1 To rws, clm)
For i = 1 To rws
For clm = 1 To 2
SERnumber(rws, clm) = Cells(rws, clm).Value
Next clm
Next i
' Loop through the array and add the names to a string
strMessage = "Here are the results:" & vbCrLf
For i = 0 To rws
strMessage = strMessage & SERnumber(i) & vbCrLf
Next 'i
MsgBox strMessage
End Sub

You can assign Excel Range to VBA Array object as shown in the following VBA code example (Range includes entire columns A and B):
Sub Range2Array()
Dim arr As Variant
arr = Range("A:B").Value
'alternatively
'arr = Range("A:B")
'test
Debug.Print (arr(1, 1))
End Sub
Such direct assignment to 2d-array has tremendous performance advantage vs using the Range iteration. You can then perform all necessary operations on array elements instead of range cells (it also will be really fast in comparison to iterative range ops).
Another useful technique is to assign Excel's UsedRange to VBA Array:
arr = ActiveSheet.UsedRange
And, the most trivial example (pertinent to your case of 2 columns, 8 rows):
arr = Range("A1:B8").Value
Hope this will help. Best regards,

Related

Create a dictionary with value from an Array

I am working on a report and trying to output my data like this
Input:
Output
I am thinking of using a dictionary with each key has multiple value of Items&their but i don't know how to implement.
Using a dictionary would not work for your chart example. A dictionary is used to hold ONLY TWO string values using a hashtable. An example of using a dictionary properly is using the two words "hello" and "a polite greeting to another human". The first string is for the word and the second is the definition of the word, just like a regular dictionary.
I would provide you with two better options. You could create a 2D array which holds all your values, but you would need to output the labels separately from the values. You could also create a file which holds the labels and the values for your chart.
If you need more help, please provide more information on what you are trying to do with the chart displayed. An array is better for calculations but a file is better for storing the values and displaying the chart.
Second answer would be to use a dictionary of dictionaries. Again input data is
With the following code
Sub DemoDictOfDict()
Dim rg As Range, rgHdr As Range
Dim dict As Dictionary
Dim sngDict As Dictionary
Dim wkb As Workbook, wks As Worksheet
Set wkb = ActiveWorkbook
Set wks = wkb.ActiveSheet
Set rg = wks.Range("A1").CurrentRegion
' Retrieving the header line from the data
Set rgHdr = rg.Rows(1).Offset(, 1).Resize(, rg.Columns.Count - 1)
Dim vHdr As Variant
vHdr = rgHdr.Value
Dim sngRow As Range
Dim sngCell As Range
Dim vdat As Variant
Dim k As Long
' Creating the dictionary which will be used
' to hold the dictionaries with the data
Set dict = New Dictionary
' Looping through the rows of the data
' as we are not using the listobject one has to use offset and resizte
For Each sngRow In rg.Offset(1).Resize(rg.Rows.Count - 1).Rows
' getting the number for different columns
' again listobject not used, so working with resizte and offset
vdat = sngRow.Offset(, 1).Resize(, sngRow.Columns.Count - 1)
' Creating the dictionary containing the numbers
Set sngDict = New Dictionary
For k = LBound(vHdr, 2) To UBound(vHdr, 2)
' Adding the values to the "inner" dictionary
sngDict.Add vHdr(1, k), vdat(1, k)
Next k
' Adding the dictionary to the dictionary
Set dict(sngRow.Cells(1, 1).Value) = sngDict
Next
' output to immediate windows
Dim outName As String, vDict As Dictionary
Dim key As Variant, i As Long
For i = 0 To dict.Count - 1
outName = dict.Keys(i)
Set vDict = dict.Items(i)
Debug.Print outName
For Each key In vDict.Keys
Debug.Print key, vDict(key)
Next
Next i
End Sub
you get the following output in the immediate window
First answer as said in one of my comments you could use Power Query to unpivot the data. As example data I took data like this
After unpivoting the data with PowerQuery (goto Data then From Table/Range, select the columns with Apple, Orange etc. in the Power Query Editor, select the tab Transform and there Unpivot columns) one gets
Then you pivot the data again and you get
Unless you're going to do something else with the collected data then there's no need for any intermediate data structure:
Dim data, r As Long, c As Long
data = ActiveSheet.Range("A3:D5").Value
For r = 2 To UBound(data, 1)
Debug.Print data(r, 1)
For c = 2 To UBound(data, 2)
Debug.Print data(1, c) & " " & data(r, c)
Next c
Debug.Print "Checked."
Next r
The location of the data should be the same as the picture to work.
Sub test()
Dim vDB As Variant
Dim vR() As Variant
Dim i As Long, r As Long, n As Long
Dim c As Integer, j As Integer
vDB = Range("a3").CurrentRegion
r = UBound(vDB, 1)
c = UBound(vDB, 2)
For i = 3 To r
n = n + 1
ReDim Preserve vR(1 To n)
vR(n) = vDB(i, 1)
For j = 2 To c
n = n + 1
ReDim Preserve vR(1 To n)
If vDB(i, j) = "" Then
vR(n) = "Checked"
Else
vR(n) = vDB(2, j) & Space(1) & vDB(i, j)
End If
Next j
Next i
Sheets.Add
Range("a1").Resize(n) = WorksheetFunction.Transpose(vR)
End Sub
Result Sheet

Excel VBA looping: reshape column into table

I have not used VBA for sometime so am very rusty... What I have is a number of records stored vertically (in a single column) and I want to use VBA to stack them side by side (into a table).
My general thoughts about how this would flow:
Start at first range
Copy data
Paste data in cell B3 of output page (just named Sheet2)
Loop back to previous range and offset by 51 rows
Copy data
Paste data in cell C3 of output page (offset by 1 column each time)
My attempt so far:
Sub Macro1()
FiftyOne = 51 ' Offset by 51 rows for every chunk
StartRange = "L262:L303" ' Start at this range of data to copy, each chunk is identical in size
OutputRange = B3 ' Paste in output at B3, but need to offset by one column each time
Range(StartRange).Offset(FiftyOne, 0).Select
Selection.Copy
Sheets("Sheet2").Select
Range("B3").Offset(0, 1).Select
ActiveSheet.Paste
End Sub
I know this is a rather lame attempt to tackle this flow, but I am really struggling with how to loop through this. I would appreciate some advice on how to do this, or a better approach to the general flow.
Edit after accepting Wolfie's answer:
I want to assign column headings, by getting the values from C258 and looping down (in a similar way to before) 51 rows at a time, to paste into row 2 of sheet2 (B2, C2, ...).
Here is my current attempt:
Sub NameToTable()
' Assign first block to range, using easily changable parameters
' Remember to "Dim" all of your variables, using colon for line continuation
Dim blocksize As Long: blocksize = 51
Dim firstrow As Long: firstrow = 258
Dim rng As Range
Set rng = ThisWorkbook.Sheets("Sheet1").Range("C" & firstrow & blocksize - 1)
' tablestart is the upper left corner of the "pasted" table
Dim tablestart As Range: Set tablestart = ThisWorkbook.Sheets("Sheet2").Range("B2")
Dim i As Long ' Looping variable i
Dim nblocks As Long: nblocks = 10 ' We're going to loop nblocks number of times
For i = 0 To nblocks - 1
' Do the actual value copying, using Resize to set the number of rows
' and using Offset to move down the original values and along the "pasted" columns
tablestart.Offset(0, i).Resize(blocksize, 1).Value = _
rng.Offset(blocksize * i, 0).Value
Next i
End Sub
Your logic seems alright, this code will create a 51 x n table, lining up each vertical block of 51 cells in its own column.
Note, it's much quicker to assign the .Value than copying and pasting, if you need formats too then you could copy/paste or similarly set format properties equal.
Sub ColumnToTable()
' Assign first block to range, using easily changable parameters
' Remember to "Dim" all of your variables, using colon for line continuation
Dim blocksize As Long: blocksize = 51
Dim firstrow As Long: firstrow = 262
Dim rng As Range
Set rng = ThisWorkbook.Sheets("Sheet1").Range("L" & firstrow & ":L" & firstrow + blocksize - 1)
' tablestart is the upper left corner of the "pasted" table
Dim tablestart As Range: Set tablestart = ThisWorkbook.Sheets("Sheet2").Range("B3")
Dim i As Long ' Looping variable i
Dim nblocks As Long: nblocks = 10 ' We're going to loop nblocks number of times
For i = 0 To nblocks - 1
' Do the actual value copying, using Resize to set the number of rows
' and using Offset to move down the original values and along the "pasted" columns
tablestart.Offset(0, i).Resize(blocksize, 1).Value = _
rng.Offset(blocksize * i, 0).Value
Next i
End Sub
Set the nblocks value to suit your needs, this is the number of resulting columns in your output table. You could get it dynamically by knowing the number of rows in the original column. Or you could use some while logic, careful to make sure that it does eventually exit of course!
Dim i As Long: i = 0
Do While rng.Offset(blocksize*i, 0).Cells(1).Value <> ""
tablestart.Offset(0, i).Resize(blocksize, 1).Value = rng.Offset(blocksize * i, 0).Value
i = i + 1
Loop
Edit: to get your column headings, keep in mind that the column headings are only 1 cell, so:
' Change this:
Set rng = ThisWorkbook.Sheets("Sheet1").Range("C" & firstrow & blocksize - 1)
' To this:
Set rng = ThisWorkbook.Sheets("Sheet1").Range("C" & firstrow)
Tip: + is used for adding numerical values, whilst & is used for concatenating stings.
Now when you're looping, you don't need the Resize, because you are only assigning 1 cell's value to 1 other cell. Resulting sub:
Sub NameToTable()
Dim blocksize As Long: blocksize = 51
Dim firstrow As Long: firstrow = 258
Dim rng As Range
Set rng = ThisWorkbook.Sheets("Sheet1").Range("C" & firstrow)
Dim tablestart As Range: Set tablestart = ThisWorkbook.Sheets("Sheet2").Range("B2")
Dim i As Long: i = 0
Do While rng.Offset(blocksize*i, 0).Value <> ""
tablestart.Offset(0, i).Value = rng.Offset(blocksize * i, 0).Value
i = i + 1
Loop
End Sub
When dealing with your worksheets in excel, each time you reference them adds overhead and slows down the code, what you want to do is take all of the info off your spreadsheet into an array then use Application.Transpose to transpose it for you.
You can then use 'Resize' to make sure your destination range is the same size and set the values.
Sub CopyAndTransRange(src As Range, dest As Range)
Dim arr As Variant 'Needs to be a variant to take cell values
arr = Application.Transpose(src.Value) 'Set to array of values
On Error GoTo eh1dim 'Capture error from vertical 1D range
dest.Resize( _
UBound(arr, 1) - LBound(arr, 1) + 1, _
UBound(arr, 2) - LBound(arr, 2) + 1 _
) = arr 'Set destination to array
Exit Sub
eh1dim:
dest.Resize( _
1, _
UBound(arr) - LBound(arr) + 1 _
) = arr 'Set row to 1D array
End Sub
Note, Application.Transpose will fall over with some arrays in weird circumstances like if there is more than 255 characters in a string in the given array, for those situations you can write your own Transpose function to flip the array for you.
Edit:
When you feed a vertical 1-dimensional range and transpose it, VBA converts it to a 1-dimensional array, I've rewritten so it captures the error when this happens then adjusts accordingly.
Just made this example which has values 1 through 7 populated on the first 7 rows of column A. This code effectively loops through each of the values, and transposes horizontally so all values are on a single row (1).
Dim rng As Range
Dim crng As Range
Static value As Integer
Set rng = ActiveSheet.Range("A1", Range("A1").End(xlDown))
For Each crng In rng.Cells
ActiveSheet.Range("A1").Offset(0, value).value = crng.value
If value <> 0 Then
crng.value = ""
End If
value = value + 1
Next crng
First we grab the required range and then iterate through each cell. Then using the offset method and an incrementing integer, we can assign their values horizontally to a single row.
It's worth noting that this would work when trying to transpose both vertically and horizontally. The key is the offset(column, row).
Just adjust where you place your incrementing Integer.
Hope this helps.

Invalid procedure call or argument in excel vba

I am using a filter on column1 of sheet1 with a unique no, get filtered values from column Z into an array (arr) object and paste those values as a string in one cell in "Dashboard" sheet. Repeat this process for all unique values on column 1 of sheet1. I am getting multiple errors 1. "Invalid Procedure call or argument" at Join() method. 2. Getting values into rng object and array. Can I get your help on where am I going wrong with this. Many Thanks.
Dim d As Object, c As Range, k, tmp As String
Dim TestRg As Range
Dim arr() As Variant
Dim i As Integer
Dim myCell As Range
Dim rng As Range
i = 2
Set d = CreateObject("scripting.dictionary")
Columns(1).Select
For Each c In Selection
tmp = Trim(c.Value)
If Len(tmp) > 0 Then d(tmp) = d(tmp) + 1
Next c
For Each k In d.Keys
If IsNumeric(k) Then
Set TestRg = Range("A1:AQ" & LastRow(ActiveSheet))
TestRg.AutoFilter Field:=1, Criteria1:=k, Operator:=xlFilterValues
LasRow = ActiveSheet.UsedRange.Rows(ActiveSheet.UsedRange.Rows.Count).Row
Set rng = ActiveSheet.Range("Z1" & ":" & "Z" & LasRow).SpecialCells(xlCellTypeVisible)
rng.Activate
arr = rng.Value
Worksheets("Dashboard").Range("A" & i) = k
Worksheets("Dashboard").Range("E" & i).Resize(UBound(arr, 1)).Value = Join(arr, " ")
i = i + 1
Erase arr
End If
Next k
as for the very error your post is asking help for, it comes out of:
Worksheets("Dashboard").Range("E" & i).Resize(UBound(arr, 1)).Value = Join(arr, " ")
since arr is a Two-dimensional array while Join() function requires a One-dimensional one
you then should:
declare arr as a simple Variant:
Dim arr As Variant
fill it in a way so as to generate a One-dimensional array
arr = Application.Transpose(rng.Value)
other than that it seems to me your code have some more issues
you may want to fix this error first and then, should those issues actually arise and shouldn't you be able to fix, make a new post

COUNTIF() in 'For' loop

I have a column with nearly 100k and am trying to determine how many times a value occurs repeatedly in that column. I can do it row by row currently, but this is menial as a programmer, through something like =COUNTIF(D:D,D2). Yet that only returns D2 matches in column D.
I need to iterate through all values of D returning countif, therefore revealing all of the values repetitions in the column. I can remove duplicates later! So I have a dev. button a basic sub, or function (man this is new to me) and something along the lines of the most basic for loop ever. Just getting caught up on how to implement the COUNTIF() to to the loop properly.
Right now I'm looking at:
Sub doloop()
Dim i As Integer
i = 1
Do While i < D.Length
Cells(i, 8).Value =CountIf(D:D,D[i])
i = i + 1
Loop
End Sub
That code is incorrect obviously but it is where I'm at and may help for anyone more familiar with other languages.
Use Application.WorksheetFunction.CountIf() in your loop.
Private Sub doloop()
Dim lastRow As Long
Dim d As Double
Dim r As Range
Dim WS As Excel.Worksheet
Dim strValue As String
Dim lRow As Long
'Build your worksheet object
Set WS = ActiveWorkbook.Sheets("sheet1")
'Get the last used row in column A
lastRow = WS.Cells(WS.Rows.count, "D").End(xlUp).Row
'Build your range object to be searched
Set r = WS.Range("D1:D" & lastRow)
lRow = 1
WS.Activate
'Loop through the rows and do the search
Do While lRow <= lastRow
'First, get the value we will search for from the current row
strValue = WS.Range("D" & lRow).Value
'Return the count from the CountIf() worksheet function
d = Application.worksheetFunction.CountIf(r, strValue)
'Write that value to the current row
WS.Range("H" & lRow).Value = d
lRow = lRow + 1
Loop
End Sub
I believe you are trying to write the value to the cell, that is what the above does. FYI, if you want to put a formula into the cell, here is how that is done. Use this in place of WS.Range("H" & lRow).Value = d
WS.Range("H" & lRow).Formula = "=CountIf(D:D, D" & lRow & ")"
Sounds like you may want to look into using tables in Excel and capitalizing on their features like filtering and equation autofill. You may also be interested in using a PivotTable to do something very similar to what you're describing.
If you really want to go about this the programmatic way, I think the solution Matt gives answers your question about how to do this using CountIf. There's a big detriment to using CountIf though, in that it's not very computationally efficient. I don't think the code Matt posted will really be practical for processing the 100K rows mentioned in the OP (Application.ScreenUpdating = false would help some). Here's an alternative method that's a lot more efficient, but less intuitive, so you'll have to decide what suites your needs and what you feel conformable with.
Sub CountOccurances()
'Define Input and Output Ranges
'The best way to do this may very from case to case,
'So it should be addressed seperately
'Right now we'll assume current sheet rows 1-100K as OP specifies
Dim RInput, ROutput As Range
Set RInput = Range("D1:D100000")
Set ROutput = Range("E1:E100000")
'Define array for housing and processing range values
Dim A() As Variant
ReDim A(1 To RInput.Rows.Count, 0)
'Use Value2 as quicker more accurate value
A = RInput.Value2
'Create dictionary object
Set d = CreateObject("Scripting.Dictionary")
'Loop through array, adding new values and counting values as you go
For i = 1 To UBound(A)
If d.Exists(A(i, 1)) Then
d(A(i, 1)) = d(A(i, 1)) + 1
Else
d.Add A(i, 1), 1
End If
Next
'Overwrite original array values with count of that value
For i = 1 To UBound(A)
A(i, 1) = d(A(i, 1))
Next
'Write resulting array to output range
ROutput = A
End Sub
You can also modify this to include the removal of replicates you mentioned.
Sub CountOccurances_PrintOnce()
'Define Input and Output Ranges
'The best way to do this may very from case to case,
'So it should be addressed seperately
'Right now we'll assume current sheet rows 1-100K as OP specifies
Dim RInput, ROutput As Range
Set RInput = Range("D1:D100000")
Set ROutput = Range("F1:F9")
'Define array for housing and processing range values
Dim A() As Variant
ReDim A(1 To RInput.Rows.Count, 0)
'Use Value2 as quicker more accurate value
A = RInput.Value2
'Create dictionary object
Set d = CreateObject("Scripting.Dictionary")
'Loop through array, adding new values and counting values as you go
For i = 1 To UBound(A)
If d.Exists(A(i, 1)) Then
d(A(i, 1)) = d(A(i, 1)) + 1
Else
d.Add A(i, 1), 1
End If
Next
'Print results to VBA's immediate window
Dim sum As Double
For Each K In d.Keys
Debug.Print K & ": " & d(K)
sum = sum + d(K)
Next
Debug.Print "Total: " & sum
End Sub

How to get row based on multiple criteria?

I'm trying to search a worksheet for a row where the values in the first 3 columns match a set of 3 criteria. I'm using this linear search:
Function findRow(pName as string,fNo as string,mType as string) As Long
Dim rowCtr As Long
rowCtr = 2
While Not rowMatchesCriteria(rowCtr, pName,fNo,mType)
rowCtr = rowCtr + 1
Wend
findRow=rowCtr
End Function
Function rowMatchesCriteria(row As Long, pName As String, fNo As String, mType As String) As Boolean
rowMatchesCriteria = dSheet.Cells(row,1)=pName _
And dSheet.Cells(row,2)=fNo _
And dSheet.Cells(row,3)=mType
End Function
We can assume that for any 3 criteria, there is only one match. However, this is very slow. dSheet has ~35,000 entries to search through, and I need to perform ~400,000 searches.
I looked at some of the solutions in this question, and while I'm sure that using AutoFilter or an advanced would be faster than a linear search, I don't understand how to get the index of the row that the filter returns. What I'm looking for would be:
Sub makeUpdate(c1 as string,c2 as string,c3 as string)
Dim result as long
result = findRow(c1,c2,c3)
dSheet.Cells(result,updateColumn) = someUpdateValue
End Sub
How do I actually return the result row that I'm looking for once I've applied AutoFilter?
For performance you're hard-pressed to beat a Dictionary-based lookup table:
Sub FindMatches()
Dim d As Object, rw As Range, k, t
Dim arr, arrOut, nR, n
t = Timer
'create the row map (40k rows)
Set d = GetRowLookup(Sheets("Sheet1").Range("A2:C40001"))
Debug.Print Timer - t, "map"
t = Timer
'run lookups on the row map
'(same values I used to create the map, but randomly-sorted)
For Each rw In Sheets("sheet2").Range("A2:C480000").Rows
k = GetKey(rw)
If d.exists(k) Then rw.Cells(3).Offset(0, 1).Value = d(k)
Next rw
Debug.Print Timer - t, "slow version"
t = Timer
'run lookups again - faster version
arr = Sheets("sheet2").Range("A2:C480000").Value
nR = UBound(arr, 1)
ReDim arrOut(1 To nR, 1 To 1)
For n = 1 To nR
k = arr(n, 1) & Chr(0) & arr(n, 2) & Chr(0) & arr(n, 3)
If d.exists(k) Then arrOut(n, 1) = d(k)
Next n
Sheets("sheet2").Range("D2").Resize(nR, 1).Value = arrOut
Debug.Print Timer - t, "fast version"
End Sub
'create a dictionary lookup based on three column values
Function GetRowLookup(rng As Range)
Dim d As Object, k, rw As Range
Set d = CreateObject("scripting.dictionary")
For Each rw In rng.Rows
k = GetKey(rw)
d.Add k, rw.Cells(1).Row 'not checking for duplicates!
Next rw
Set GetRowLookup = d
End Function
'create a key from a given row
Function GetKey(rw As Range)
GetKey = rw.Cells(1).Value & Chr(0) & rw.Cells(2).Value & _
Chr(0) & rw.Cells(3).Value
End Function
If you want to do an exact lookup on 3 columns, you can use VLOOKUP using a slight trick: you create a key based on your 3 columns. E.g. if you want to perform your query on columns B, C, D, create a key column in A based on your three columns (e.g. =B1&C1&D1). Then:
=VLOOKUP(lookupvalue1&lookupvalue2&lookupvalue3,A:D,{2,3,4},FALSE)
should do the magic.
One simple solution could be using excel function MATCH as array formula.
No for-each loops so I guess this could run very fast.
Formula will look e.g. like this MATCH("A"&"B"&"C",RANGE_1&RANGE_2&RANGE_3,0)
Option Explicit
Private Const FORMULA_TEMPLATE As String = _
"=MATCH(""CRITERIA_1""&""CRITERIA_2""&""CRITERIA_3"",RANGE_1&RANGE_2&RANGE_3,MATCH_TYPE)"
Private Const EXACT_MATCH = 0
Sub test()
Dim result
result = findRow("A", "B", "C")
Debug.Print "A,B,C was found on row : [" & result & "]"
End Sub
Function findRow(pName As String, fNo As String, mType As String) As Long
On Error GoTo Err_Handler
Dim originalReferenceStyle
originalReferenceStyle = Application.ReferenceStyle
Application.ReferenceStyle = xlR1C1
Dim data As Range
Set data = ActiveSheet.UsedRange
Dim formula As String
' Add criteria
formula = Replace(FORMULA_TEMPLATE, "CRITERIA_1", pName)
formula = Replace(formula, "CRITERIA_2", fNo)
formula = Replace(formula, "CRITERIA_3", mType)
' Add ranges where search
formula = Replace(formula, "RANGE_1", data.Columns(1).Address(ReferenceStyle:=xlR1C1))
formula = Replace(formula, "RANGE_2", data.Columns(2).Address(ReferenceStyle:=xlR1C1))
formula = Replace(formula, "RANGE_3", data.Columns(3).Address(ReferenceStyle:=xlR1C1))
' Add match type
formula = Replace(formula, "MATCH_TYPE", EXACT_MATCH)
' Get formula result
findRow = Application.Evaluate(formula)
Err_Handler:
' Set reference style back
Application.ReferenceStyle = originalReferenceStyle
End Function
Output: A,B,C was found on row : [4]
In order to improve the best answer (multi criteria search), you would want to check for duplicates to avoid error.
'create a dictionary lookup based on three column values
Function GetRowLookup(rng As Range)
Dim d As Object, k, rw As Range
Set d = CreateObject("scripting.dictionary")
For Each rw In rng.Rows
k = GetKey(rw)
if not d.exists(k) then
d.Add k, rw.Cells(1).Row 'checking for duplicates!
end if
Next rw
Set GetRowLookup = d
End Function