Option Explicit
Global dict As Object
Global ColumnMatchARR As Variant
Global OldDataRangeSTR As String
Function Fu_TS_DictLookup(IdRNG As Range, SourceHeaders As Range, UniqueKey As Range, DestinationHeaders As Range, Optional ReReadData As Boolean = False) As Variant()
Dim DataRange As Range, ws As Worksheet: Set ws = Worksheets(UniqueKey.Parent.Name)
Dim SourceHeadersARR As Variant, DestinationHeadersARR As Variant
Dim iDHA As Long, iSHA As Long, i As Long
Dim coT As Double: coT = Timer()
Call TurnOffFeatures
On Error GoTo ErrHand
' Checking that only Header -cell from UniqueKey -range is selected
If UniqueKey.Cells.Count > 1 Then
Set UniqueKey = UniqueKey.Cells(1)
End If
' Range where to find all "Emp ID"s
Dim IdARR As Variant: IdARR = ws.Range(UniqueKey.Offset(1, 0).Address & ":" & Split(UniqueKey.Address, "$")(1) & ws.Cells.End(xlDown).Row).Value2
' Get DataRange
Set DataRange = ws.Range(SourceHeaders.Cells(1).Offset(1, 0).Address & ":" & SourceHeaders.Cells(UBound(IdARR, 1) + 1, SourceHeaders.Columns.Count).Address)
' Read headers
SourceHeadersARR = WorksheetFunction.Transpose(WorksheetFunction.Transpose(DataRange.Rows(1).Offset(-1, 0).Value2))
DestinationHeadersARR = WorksheetFunction.Transpose(WorksheetFunction.Transpose(DestinationHeaders.Value2))
'Create columns that match the table (selecting and rearranging columns)
ReDim ColumnMatchARR(1 To UBound(DestinationHeadersARR), 1 To 2)
For iDHA = 1 To UBound(DestinationHeadersARR)
ColumnMatchARR(iDHA, 1) = DestinationHeadersARR(iDHA)
For iSHA = LBound(SourceHeadersARR) To UBound(SourceHeadersARR)
If SourceHeadersARR(iSHA) = DestinationHeadersARR(iDHA) Then
ColumnMatchARR(iDHA, 2) = iSHA
Exit For
End If
Next iSHA
Next iDHA
' Checking that the headers of the target area can be found in the source area.
For i = 1 To UBound(DestinationHeadersARR)
If IsEmpty(ColumnMatchARR(i, 2)) Then
MsgBox "There is no title in the data area for corresponding the title of the return value: " & ColumnMatchARR(i, 1) & vbCrLf & vbCrLf & " Destination Headers must be found from Source Headers", , "ERROR ON DATA HEADERS!": End
End If
Next i
' If dict Is Nothing Or IsEmpty(ColumnMatchARR) Or ReReadData Then ' Read Data from Sheet: REMOVED! Headers data is checked every time.
If dict Is Nothing Or ReReadData Or OldDataRangeSTR <> DataRange.Address Then ' Read Data from Sheet
Debug.Print "No data on memory or data refresh demanded, reading data"
' Creating Dictionary
Set dict = CreateObject("Scripting.Dictionary")
' Reading all data to dictionary
On Error GoTo ErrHandDublicates
For i = 1 To UBound(IdARR, 1)
dict.Add CStr(IdARR(i, 1)), DataRange.Rows(i).Value2
Next
On Error GoTo -1
Else
Debug.Print "Data allready in Dictionary"
End If
'Creating variables
Dim j As Long, FuRows As Long: FuRows = IdRNG.Cells.Count
Dim HeadersCount As Long: HeadersCount = UBound(DestinationHeadersARR)
Dim EmpIdARR As Variant, RetArrD2 As Variant: ReDim RetArrD2(1 To FuRows, 1 To HeadersCount)
' Creating LookUp -values ARRAY
If FuRows = 1 Then
ReDim EmpIdARR(1 To 1, 1 To 1) ' Forcing Variant to array
EmpIdARR(1, 1) = IdRNG.Value2 ' LookUp -array get single value for search
Else
EmpIdARR = IdRNG.Value2 ' LookUp -array range of values for search
End If
' Writing LookUp -search return values to Array
Dim MissingID As Variant, MissingROW As Long
For i = 1 To UBound(EmpIdARR, 1)
For j = 1 To HeadersCount
If dict.Exists(CStr(EmpIdARR(i, 1))) Then
RetArrD2(i, j) = dict(CStr(EmpIdARR(i, 1)))(1, ColumnMatchARR(j, 2))
Else
MissingID = CStr(EmpIdARR(i, 1)) ' This Lookup value is not found from the data!
MissingROW = i + 1 ' A row with a missing Lookup value
RetArrD2(i, j) = "" ' Turns missing Lookup result to empty string.
End If
Next j
If MissingROW > 0 Then Debug.Print "LookUp ID: " & MissingID & " at row: " & MissingROW & " not found from data!": MissingROW = 0
Next i
' The return value of Fu_TS_DictLookup is a 2-dimensional RetArrD2 array
Fu_TS_DictLookup = RetArrD2
OldDataRangeSTR = DataRange.Address
Debug.Print "Execution of the function Fu_TS_DictLookup took: " & Timer() - coT & " seconds."
ErrHand:
Call TurnOnFeatures
Exit Function
ErrHandDublicates:
Debug.Print "Error number: " & Err.Number & " " & Err.Description: MsgBox "Value: " & IdARR(i, 1) & " at row: " & WorksheetFunction.Match(IdARR(i, 1), IdARR, 0) + 1 & " have dublicate on row: " & i + 1 & vbCrLf & vbCrLf & " This function does not accept duplicates in Key values!", , "ERROR ON DATA!": Call TurnOnFeatures: End
End Function
Function TurnOffFeatures()
Application.Calculation = xlManual
Application.DisplayAlerts = False
Application.EnableEvents = False
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
End Function
Function TurnOnFeatures()
Application.Calculation = xlAutomatic
Application.DisplayAlerts = True
Application.EnableEvents = True
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
End Function
Sub test_Fu_TS_DictLookup() ' Only for Testing from vba
Dim x As Variant
Dim LookUpValuesRNG As Range, SourceHeadersRNG As Range, UniqueKeyRNG As Range, DestinationHeadersRNG As Range, DestinationRNG As Range
Dim wsData As Worksheet, wsReturnValue As Worksheet
Set wsData = Worksheets("Sheet4") ' Sheet where Data found
Set wsReturnValue = Worksheets("Sheet5") ' Sheet where writing LookUp data back
Set LookUpValuesRNG = wsReturnValue.Range("A2:A" & wsReturnValue.Range("A2").Cells.End(xlDown).Row) ' Values to search for
Set SourceHeadersRNG = wsData.Range("A1:G1") ' Data headers
Set UniqueKeyRNG = wsData.Range("D1") ' Header for Data Unique Key
Set DestinationHeadersRNG = wsReturnValue.Range("B1:G1") ' The headers of the values to return from dictionary
x = Fu_TS_DictLookup(LookUpValuesRNG, SourceHeadersRNG, UniqueKeyRNG, DestinationHeadersRNG)
Set DestinationRNG = wsReturnValue.Range("B2:G" & LookUpValuesRNG.Cells.Count + 1)
DestinationRNG.Value2 = x ' Writing LookUp data back to Sheet
End Sub