简体   繁体   English

VBA 中的 Vlookup 与 ARRAYS 中的 APROXIMATE MATCH 等效

[英]Vlookup equivalent in VBA with APROXIMATE MATCH in ARRAYS

Summary of the problem is:问题总结如下: 在此处输入图片说明

I have come up with the following VBA code that is working fine but taking too long to run.我想出了以下 VBA 代码,它运行良好,但运行时间太长。 So, I am trying to process all the data taking into arrays.所以,我正在尝试处理所有进入数组的数据。 But I am stuck with Vlookup APPROXIMATE MATCH in ARRAYS .但是我坚持使用ARRAYS 中的 Vlookup APPROXIMATE MATCH

The working VBA code is:工作的 VBA 代码是:

Option Explicit

Sub VlookupAlternative()
    
    Const INPUT_SHT = "shtSrc"
    Const OUTPUT_SHT = "shtDest"
    
    
    Dim wb As Workbook, wsIn As Worksheet, wsOut As Worksheet
    Dim rngSrc As Range, rngDest As Range, celSrc As Range, celDest As Range
    Dim rLastIn As Long, cLastIn As Long
    Dim rLastOut As Long, cLastOut As Long

    Application.Calculation = xlCalculationManual
    Application.DisplayStatusBar = False
    Application.EnableEvents = False
    Application.ScreenUpdating = False

    On Error Resume Next
    
    Set wb = ThisWorkbook
    Set wsIn = wb.Sheets(INPUT_SHT)
    Set wsOut = wb.Sheets(OUTPUT_SHT)
    
    
    rLastIn = lastRow(wsIn)
    cLastIn = LastCol(wsIn)
    
    rLastOut = lastRow(wsOut)
    cLastOut = LastCol(wsOut)
    
    With wb

        Set rngSrc = wsIn.Range("$A$2:$F$" & rLastIn)
        Set rngDest = wsOut.Range("$B$2:$D$" & rLastOut)
        
        
        ' Compare top headers and left headers respectively. If matching, copy the value in destination table.
        For Each celDest In rngDest
            For Each celSrc In rngSrc
                If wsIn.Cells(celSrc.Row, 1).Value = Application.IfError(Application.VLookup(CDbl(TimeValue(wsOut.Cells(celDest.Row, 1).Value)), rngSrc, 1, True), "") And _
                    wsIn.Cells(celSrc.Row, 3).Value = Format(wsOut.Cells(celDest.Row, 1).Value, "DDDD") And _
                    wsIn.Cells(1, celSrc.Column).Value = wsOut.Cells(1, celDest.Column).Value Then
                    celDest.Value = celSrc.Value
                End If
            Next celSrc
        Next celDest
    End With
    
    Application.Calculation = xlCalculationAutomatic
    Application.DisplayStatusBar = True
    Application.EnableEvents = True
    Application.ScreenUpdating = True

End Sub


Function lastRow(sh As Worksheet)
    On Error Resume Next
    lastRow = sh.Cells.Find(What:="*", _
                            After:=sh.Range("A1"), _
                            Lookat:=xlPart, _
                            LookIn:=xlFormulas, _
                            SearchOrder:=xlByRows, _
                            SearchDirection:=xlPrevious, _
                            MatchCase:=False).Row
    On Error GoTo 0
End Function


Function LastCol(sh As Worksheet)
    On Error Resume Next
    LastCol = sh.Cells.Find(What:="*", _
                            After:=sh.Range("A1"), _
                            Lookat:=xlPart, _
                            LookIn:=xlFormulas, _
                            SearchOrder:=xlByColumns, _
                            SearchDirection:=xlPrevious, _
                            MatchCase:=False).Column
    On Error GoTo 0
End Function

The code I am trying to write with arrays :我试图用数组编写的代码:

Option Explicit

Sub VlookupAlternativeArray()
    
    Const INPUT_SHT = "shtSrc"
    Const OUTPUT_SHT = "shtDest"
    
    
    Dim wb As Workbook, wsIn As Worksheet, wsOut As Worksheet
    Dim rngSrc As Range, rngDest As Range, rngLookup As Range, rngReturn As Range
    Dim celSrc As Range, celDest As Range
    Dim rLastIn As Long, cLastIn As Long
    Dim rLastOut As Long, cLastOut As Long
    Dim lookupArray As Variant, returnArray As Variant, destArray As Variant

    Application.Calculation = xlCalculationManual
    Application.DisplayStatusBar = False
    Application.EnableEvents = False
    Application.ScreenUpdating = False

    On Error Resume Next
    
    Set wb = ThisWorkbook
    Set wsIn = wb.Sheets(INPUT_SHT)
    Set wsOut = wb.Sheets(OUTPUT_SHT)
    
    
    rLastIn = lastRow(wsIn)
    cLastIn = LastCol(wsIn)
    
    rLastOut = lastRow(wsOut)
    cLastOut = LastCol(wsOut)
    
    Set rngLookup = wsIn.Range("$A$2:$C$" & rLastIn)
    Set rngReturn = wsIn.Range("$D$2:$F$" & rLastIn)
    Set rngDest = wsOut.Range("$B$2:$D$" & rLastOut)

    
    lookupArray = rngLookup.Value2
    returnArray = rngReturn.Value2
    destArray = rngDest.Value2

'**********I want to put a vlookup approximate equivalent code here.*************************************************

                                            '    Dim desc As String
                                            '    Dim i As Long
                                            '    Dim j As Long
                                            '    For i = LBound(destArray, 1) To UBound(destArray, 1)
                                            '        desc = destArray(i, 1)
                                            '        For j = LBound(lookupArray, 1) To UBound(lookupArray, 1)
                                            '            If InStr(1, desc, lookupArray(j, 1), vbTextCompare) > 0 Then
                                            '                destArray(i, 2) = returnArray(j, 1)
                                            '                Exit For
                                            '            End If
                                            '        Next j
                                            '    Next i
'*********************************************************************************************************************

    wsOut.Range("B2").Resize(UBound(destArray, 1), 1).Value2 = Application.Index(destArray, 0, 2)

    Application.Calculation = xlCalculationAutomatic
    Application.DisplayStatusBar = True
    Application.EnableEvents = True
    Application.ScreenUpdating = True

End Sub


Function lastRow(sh As Worksheet)
    On Error Resume Next
    lastRow = sh.Cells.Find(What:="*", _
                            After:=sh.Range("A1"), _
                            Lookat:=xlPart, _
                            LookIn:=xlFormulas, _
                            SearchOrder:=xlByRows, _
                            SearchDirection:=xlPrevious, _
                            MatchCase:=False).Row
    On Error GoTo 0
End Function


Function LastCol(sh As Worksheet)
    On Error Resume Next
    LastCol = sh.Cells.Find(What:="*", _
                            After:=sh.Range("A1"), _
                            Lookat:=xlPart, _
                            LookIn:=xlFormulas, _
                            SearchOrder:=xlByColumns, _
                            SearchDirection:=xlPrevious, _
                            MatchCase:=False).Column
    On Error GoTo 0
End Function

As a beginner with VBA, I need a direction how to approach Vlookup APPROXIMATE MATCH in ARRAYS.作为 VBA 的初学者,我需要指导如何在 ARRAYS 中处理 Vlookup APPROXIMATE MATCH。 Does Application.Vlookup work in Arrays? Application.Vlookup在数组中工作吗? Is VBA Dictionary a better option here? VBA 字典是更好的选择吗? Any code sample or advice will be much appreciated.任何代码示例或建议将不胜感激。

If you need a reference to the context of the code, it is detailed here .如果您需要对代码上下文的引用,请在此处详细说明。

There is no equivalent to VLOOKUP in VBA. VBA 中没有与 VLOOKUP 等效的东西。 Instead, Excel's own VLOOKUP can be used by VBA, but not its ISERROR() function.相反,VBA 可以使用 Excel 自己的 VLOOKUP,但不能使用它的 ISERROR() 函数。 Your code runs slowly because it crashes on each attempt of running your formula and On Error Resume Next prevents it from stopping.您的代码运行缓慢,因为它在每次尝试运行您的公式时都会崩溃,而On Error Resume Next阻止它停止。 It doesn't work.它不起作用。 And it can't work because the ISERROR condition not only can't work in VBA but also can't make sense within your formula.它无法工作,因为 ISERROR 条件不仅无法在 VBA 中工作,而且在您的公式中也没有意义。

Not clear what you want to achieve but consider using Application.Match if you need approximate matches or Range.Find for exact ones.不清楚您想要实现的目标,但如果您需要近似匹配或Range.Find来获得精确匹配,请考虑使用Application.Match

Note that wsIn.Cells(celSrc.Row, 1) and wsOut.Cells(celDest.Row, 1) are circumscribing the ranges celSrc and celDest .请注意, wsIn.Cells(celSrc.Row, 1)wsOut.Cells(celDest.Row, 1)限定了celSrccelDest范围。 Further, consider the difference between a cell's Value , Value2 , Formula and its display.此外,请考虑单元格的ValueValue2Formula及其显示之间的差异。 When searching for dates or times Value2 holds a number, Value holds the formatted expression of that number (the displayed value).搜索日期或时间时, Value2包含一个数字, Value包含该数字的格式化表达式(显示的值)。 Formula may hold a copy of Value2 or the function that created Value2 .公式可能包含Value2的副本或创建Value2的函数。

Searching for both number and format makes no sense because they are essentially the same.搜索数字和格式是没有意义的,因为它们本质上是相同的。 But you do need to know which format of your search criterium you are looking for and in which property to find it.但是您确实需要知道您正在寻找哪种格式的搜索标准以及在哪个属性中找到它。 VBA's Range.Find allows you to search in values or formulas, the latter giving you access to Value2 (if no functions are used in the search data). VBA 的Range.Find允许您搜索值或公式,后者使您可以访问Value2 (如果搜索数据中未使用任何函数)。

Finally, By assigning a WorksheetFunction.Match result to a variable of variant data type, like ...最后,通过将WorksheetFunction.Match结果分配给变体数据类型的变量,例如 ...

Dim Match As Variant
Match = Application.Match(celSrc.Value, RngDest, 0)

the failure to match will result in vbError being assigned to the variable.匹配失败将导致vbError被分配给变量。 You can trap that error with ...你可以用 ...

If Match = vbError Then
    Match = 1      ' rectify the condition
End if

No On Error Resume Next is involved here because the Variant can itself store the error.这里不涉及On Error Resume Next ,因为 Variant 本身可以存储错误。 Note that this will not work if you use WorksheetFunction.Match or if you assign the returned value to a variable of Long data type.请注意,如果您使用WorksheetFunction.Match或将返回值分配给 Long 数据类型的变量,这将不起作用。 Use On Error Resume Next only where you know which error to expect and have made provision for it to be handled by your code.仅在您知道预期会出现哪个错误并已为由您的代码处理该错误做好准备时才使用On Error Resume Next Don't use it prophylactically.不要预防性地使用它。 It doesn't work, as shown here.它不起作用,如下所示。 Instead it blindfolds you.相反,它会蒙住你的眼睛。

Finally, each reference to the sheet is slow.最后,对工作表的每次引用都很慢。 Therefore code will run significantly faster if many references can be made to arrays instead of cells.因此,如果可以对数组而不是单元格进行多次引用,则代码运行速度会明显加快。 Indeed you could load your lookup values into arrays but not the lookup ranges.实际上,您可以将查找值加载到数组中,但不能将查找范围加载到数组中。 For now, your problem isn't that VLOOKUP doesn't work on arrays - which indeed it doesn't because arrays are VBA and ranges are Excel - but that your lookup doesn't work.目前,您的问题不在于 VLOOKUP 不适用于数组 - 这确实不起作用,因为数组是 VBA 而范围是 Excel - 但您的查找不起作用。 Make your function work first and start worrying about speed thereafter.先让你的函数工作,然后开始担心速度。

Please read up on the presentation of dates and times in Excel.请仔细阅读 Excel 中日期和时间的呈现方式。 You will find that "03/01/2021 08"20 AM" in your ShSrc!A2 actually is an interpretation of the number 44199.3472222222 where 44199 describes the day and 0.347222222 is the time as a fraction of 1 (meaning 24 hours =1). This number is the Value of that cell which is given the format "mm/dd/yyyy hh:mm AM/PM".你会发现你的 ShSrc!A2 中的 "03/01/2021 08"20 AM" 实际上是对数字 44199.3472222222 的解释,其中 44199 描述了一天,0.347222222 是时间的 1 的分数(意味着 24 小时 =1) . 此数字是该单元格的Value ,格式为“mm/dd/yyyy hh:mm AM/PM”。

It follows that the formula in C2 is (or should be) =A2 with a custom cell format of dddd which will display the same underlying number as "Sunday".因此,C2 中的公式是(或应该是) =A2 ,具有dddd自定义单元格格式,它将显示与“星期日”相同的基础数字。

It further follows that all the times in ShSrc!A:B should be Date/Time values, as opposed to mere Time values that omit the day.进一步推断,ShSrc!A:B 中的所有时间都应该是日期/时间值,而不是仅仅省略一天的时间值。 This is a problem of data capture which we don't deal with here.这是我们不在这里处理的数据捕获问题。 The same applies to cell ShDest!A2.这同样适用于单元格 ShDest!A2。

Now that all your data are true date/times you can search and compare them.现在您的所有数据都是真实的日期/时间,您可以搜索和比较它们。 This is the formula for ShDest!B2.这是 ShDest!B2 的公式。

=VLOOKUP($A2, ShSrc!$A$2:$F$5,COLUMN()+2,TRUE)

The formula is in column B. Therefore COLUMN() returns the number 2. The lookup column in ShSrc!$A$2:$F$5 is column D which has the number 4 which is equal to COLUMN()+2 you find in the formula.公式在 B 列中。因此COLUMN()返回数字 2。ShSrc!$A$2:$F$5 中的查找列是 D 列,其中的数字 4 等于 COLUMN()+2 您在公式。 This little trick allows you to copy the formula from ShDest!B2 to C2:D2 where COLUMN() changes to 3, 4 and 5 and COLUMN() + 2, therefore, to 5, 6 and 7.这个小技巧允许您将公式从 ShDest!B2 复制到 C2:D2,其中 COLUMN() 更改为 3、4 和 5,而 COLUMN() + 2 因此更改为 5、6 和 7。

Frankly, I can't see how we can connect the above to your request to assistance with writing code.坦率地说,我不明白我们如何将上述内容与您的请求联系起来,以协助编写代码。 The immediate future requires determination of whether and why code is required at all, and if the answer is yes, any question you might ask would have to take the above into consideration, to wit, include the nature of your data.不久的将来需要确定是否以及为什么需要代码,如果答案是肯定的,那么您可能提出的任何问题都必须考虑到上述内容,也就是说,包括您的数据的性质。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM