简体   繁体   English

Excel 需要很长时间来计算 UDF VBA

[英]Excel taking really long to calculate a UDF VBA

The file name I'm trying to match is on Row A and I'm looking through Row I to see if there is a match I found this code I can't recall where but I am trying to match row of part numbers to a row of its image file names.我要匹配的文件名在 A 行,我正在查看第 I 行是否有匹配项其图像文件名的行。 This code works, however, there is a problem when I run it it takes really long to calculate even just 1 column and when I do hundreds at a time my excel just stops responding, and I have thousands of products I need to match.这段代码有效,但是,当我运行它时存在一个问题,即使只计算 1 列也需要很长时间,当我一次执行数百个时,我的 excel 停止响应,并且我需要匹配数千种产品。 I am really new with VBA so I can't even figure out the problem.我对 VBA 真的很陌生,所以我什至无法弄清楚问题所在。

Please help, thank you.请帮忙,谢谢。

'Name function and arguments
Function SearchChars(lookup_value As String, tbl_array As Range) As String
'Declare variables and types
Dim i As Integer, str As String, Value As String
Dim a As Integer, b As Integer, cell As Variant
'Iterste through each cell
For Each cell In tbl_array
  'Save cell value to variable
  str = cell
  'Iterate through characters
  For i = 1 To Len(lookup_value)
    'Same character?
    If InStr(cell, Mid(lookup_value, i, 1)) > 0 Then
      'Add 1 to number in array
      a = a + 1
      'Remove evaluated character from cell and contine with remaning characters
      cell = Mid(cell, 1, InStr(cell, Mid(lookup_value, i, 1)) - 1) & Mid(cell, InStr(cell, Mid(lookup_value, i, 1)) + 1, 9999)
    End If
  'Next character
  Next i
 
a = a - Len(cell)
'Save value if there are more matching characters than before  
If a > b Then
  b = a
  Value = str
End If
 
a = 0
Next cell
'Return value with the most matching characters
SearchChars = Value
End Function

As said, minimizing the interactions with the sheet by assigning the range to an array will structurally make your macros faster.如前所述,通过将范围分配给数组来最小化与工作表的交互将使您的宏在结构上更快。 Not tested but these minor changes in your code should help you on the right track:未经测试,但您的代码中的这些细微更改应该可以帮助您走上正轨:

    Option Explicit
    'Name function and arguments
    Function SearchChars2(lookup_value As String, tbl_array As Range) As String
    'Declare variables and types
    Dim i As Integer, str As String, Value As String
    Dim a As Integer, b As Integer, cell As Variant
    'Iterste through each cell => replace with array
    'adapt to correct sheet
    Dim arr
    arr = tbl_array
    
    For Each cell In arr 'tbl_array
        'Save cell value to variable
        str = cell
        'Iterate through characters
        For i = 1 To Len(lookup_value)
          'Same character?
          If InStr(cell, Mid(lookup_value, i, 1)) > 0 Then
            'Add 1 to number in array
            a = a + 1
            'Remove evaluated character from cell and contine with remaning characters
            cell = Mid(cell, 1, InStr(cell, Mid(lookup_value, i, 1)) - 1) & Mid(cell, InStr(cell, Mid(lookup_value, i, 1)) + 1, 9999)
          End If
        'Next character
        Next i
     
        a = a - Len(cell)
        'Save value if there are more matching characters than before
        If a > b Then
          b = a
          Value = str
        End If
         
        a = 0
        Next cell
    'Return value with the most matching characters
    SearchChars2 = Value
    End Function

I was trying to modify your existing code, but I found it easier to just rewrite it using what I consider to be a better structure.我试图修改您现有的代码,但我发现使用我认为更好的结构重写它更容易。 And After running the code over 26 columns & 432 rows, It only took 0.2 seconds to find the Closest Matching String.并且在运行代码超过 26 列和 432 行之后,只需 0.2 秒即可找到最接近的匹配字符串。

I moved every value into an array.我将每个值移动到一个数组中。 I converted the lookup_value and the "cell values" into an array of bytes.我将lookup_value和“单元格值”转换为字节数组。 I compared the byte arrays to count matching "characters".我比较了字节 arrays 来计算匹配的“字符”。 And then I return the string that had the highest number of matching "characters".然后我返回匹配“字符”数量最多的字符串。

Sub Example()
    Dim StartTime As Double
    StartTime = Timer * 1000
    Debug.Print SearchChars3("Toddleson", Range("A1:Z432"))
    Debug.Print "Time Elapsed: " & Timer * 1000 - StartTime & " ms"
    'Time Elapsed: 171.875 ms
End Sub

Function SearchChars3(lookup_value As String, tbl_array As Range) As String
    Dim ClosestMatch As String, HighestMatchCount As Integer
    
    Dim tbl_values() As Variant
    tbl_values = tbl_array.Value
    
    Dim LkUpVal_Bytes() As Byte
    LkUpVal_Bytes = ToBytes(lookup_value)
    
    Dim Val As Variant
    For Each Val In tbl_values
        If Val = "" Then GoTo nextVal
        
        Dim Val_Bytes() As Byte
        Val_Bytes = ToBytes(CStr(Val))
        
        Dim MatchCount As Integer
        MatchCount = CountMatchingElements(LkUpVal_Bytes, Val_Bytes)
        
        If MatchCount > HighestMatchCount Then
            HighestMatchCount = MatchCount
            ClosestMatch = Val
        End If
nextVal:
    Next
    SearchChars3 = ClosestMatch
End Function

Function ToBytes(InputStr As String) As Byte()
    Dim ByteArr() As Byte
    ReDim ByteArr(Len(InputStr) - 1)
    Dim i As Long
    For i = 0 To Len(InputStr) - 1
        ByteArr(i) = AscW(Mid(InputStr, i + 1, 1))
    Next
    ToBytes = ByteArr
End Function

Function CountMatchingElements(Arr1 As Variant, Arr2 As Variant) As Integer
    'As elements from Arr1 are found in Arr2, those elements are removed from Arr2, to prevent re-matching with the same elements
    'To enable this feature, Arr2 is turned into a Collection
    Dim Col2 As New Collection
    Dim v As Variant
    For Each v In Arr2
        Col2.Add v
    Next
    
    Dim MatchCount As Integer, i As Long
    For Each v In Arr1
        For i = 1 To Col2.Count
            If Col2.Item(i) = v Then
                MatchCount = MatchCount + 1
                Col2.Remove (i)
                Exit For
            End If
        Next
    Next
    CountMatchingElements = MatchCount
End Function

A further optimization could be to have a second version of the ToBytes function that directly outputs the values into a Collection .进一步的优化可能是拥有ToBytes function 的第二个版本,它直接将值输出到Collection中。 Then, you can change CountMatchingElements to accept a collection and it wont need to convert the second array into a collection.然后,您可以更改CountMatchingElements以接受集合,并且不需要将第二个数组转换为集合。

I will leave that as an idea for you to experiment with.我将把它作为一个想法供你试验。

Not sure how much faster this would be (as I did it mainly to understand what you're doing.不确定这会快多少(因为我这样做主要是为了了解您在做什么。
It should be a little faster and can be refined for further performance improvements, once you post some data.一旦发布了一些数据,它应该会更快一些,并且可以进行改进以进一步提高性能。

'Name function and arguments
Function SearchChars(lookup_value As String, tbl_array As Range) As String
    
    'Declare variables and types
    Dim stVal$, stReturn$
    Dim inCharPos%, inMatchedPos%, inCountMatched%, inBestMatch%, vnCellVal
    Dim lgAR&, varDataValues()
    
    varDataValues = tbl_array.Value
    
    'Iterate through each cell
    For Each vnCellVal In varDataValues: inCountMatched = 0
      
      'Get cell value as a string (stVal)
      stVal = vnCellVal
      
        'Iterate through characters in lookup_value
        For inCharPos = 1 To Len(lookup_value)
        
            'Check is cell has any char matching this char of lookup_value?
            inMatchedPos = InStr(vnCellVal, Mid(lookup_value, inCharPos, 1))
            If inMatchedPos > 0 Then
            
                'Count number of matches
                inCountMatched = inCountMatched + 1
            
                'Remove matched char from cell value and continue
                vnCellVal = Left(vnCellVal, inMatchedPos - 1) & Mid(vnCellVal, inMatchedPos + 1, 9999)
            
            End If
        
        'Next character
        Next inCharPos
     
        'Reduce matched value by number of unmatched chars (not sure why)
        inCountMatched = inCountMatched - Len(vnCellVal)
        
        'Save return value if this is the best match so far
        If inCountMatched > inBestMatch Then
            inBestMatch = inCountMatched
            stReturn = stVal
        End If
     
    Next vnCellVal
    
    'Return value with the most matching characters
    SearchChars = stReturn

End Function

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

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