在VBA中查找一个列中元素的最快方法

huangapple go评论65阅读模式
英文:

Fastest Way to find elements of one column in another VBA

问题

The relevant part of your code compares two columns and copies matching lines. You're looking for ways to optimize it. Here's the translation of your code section:

对我实现的算法的一个部分进行比较了两个不同列表的两列,并且在找到匹配值时将它们一起复制。我想知道最快的方法是什么。
目前,它大约将大约100-150行与彼此比较,需要大约3分钟,有时更长,我觉得这相当糟糕。
这引出了一个问题,是否有比Range.Find()更快的方法,另外知道这个方法使用了什么搜索算法也很有趣。

我对VBA相当陌生,是出于需要才开始使用的,我是一名工作学生,来自c/#和python,所以我绝对不是专家,也许是我的期望太高了... 文件保存在服务器上,可能会增加运行时间,但我不确定它会增加多少。

我的代码的相关部分如下所示:

请注意,我已经将您的代码部分翻译成了中文,代码部分保持原样。

英文:

A part of an algorithm I implemented compares two columns of different lists and essentially copies the lines together if it finds matching values, and I want to know the fastest way to do this.
Currently, it compares about 100-150 lines with each other and it takes around 3 minutes, sometimes more, which I feel is pretty horrendous.
This brought up the question, if there is any faster method than Range.Find(), also it would be interesting to know what search algorithm this method is using.

I am fairly new to VBA and started using it out of necessity, I am a working student and come from c/# and python, so I am by no means an expert, maybe my expectations are just too high.. File is saved on a server so that may increase the runtime, but I am not sure by how much that factors in.

The relevant part of my code looks like this:

For Each LineA In sheet1.Range("B1:B" & LastRowSheet1)
        
        
        Set LineB = sheet2.Range("B1:B" & LastRowSheet2).Find(LineA.Value, LookIn:=xlValues)
        If Not LineB Is Nothing Then
            With sheet2
                .Range(.Cells(LineB.Row, 3), .Cells(LineB.Row, 12)).Copy sheet3.Range(sheet3.Cells(i, 4), sheet3.Cells(i, 13))
            End With
        
            With sheet1
                .Range(.Cells(LineA.Row, 2), .Cells(LineA.Row, 4)).Copy sheet3.Range(sheet3.Cells(i, 1), sheet3.Cells(i, 3))
            End With
            
            i = i + 1
            
       End If
       
    Next LineA
(changed variable names from my code, so if something doesn't make sense, tell me)

</details>


# 答案1
**得分**: 1

以下是您提供的代码的中文翻译:

```vba
尝试

    子 查找()
    Dim 数据表 As 工作表,查找表 As 工作表,结果表 As 工作表
    Dim 数据范围 As 范围,查找范围 As 范围
    Dim 数据数组 As 变体,查找数组 As 变体
    Dim i As 长整型,j As 长整型,最后一行数据 As 长整型,最后一行查找 As 长整型,找到 As 长整型
    
    设置 数据表 = 这工作簿.工作表("Sheet1")
    设置 查找表 = 这工作簿.工作表("Sheet2")
    设置 结果表 = 这工作簿.工作表("Sheet3")
    
    与 数据表
        最后一行数据 = .单元格(.行数, "B").End(xlUp).行
        设置 数据范围 = .范围("B1:B" & 最后一行数据)
        数据数组 = 数据范围.值
    结束与
    
    与 查找表
        最后一行查找 = .单元格(.行数, "B").End(xlUp).行
        设置 查找范围 = .范围("B1:B" & 最后一行查找)
        查找数组 = 查找范围.值
    结束与
    
    i = 1
    
    对于 j = 1 到 UBound(数据数组, 1)
        找到 = 应用程序.匹配(数据数组(j, 1), 查找数组, 0)
        如果 不是错误找到 然后
            结果表.范围("D" & i & ":M" & i).值 = 查找表.范围("C" & 找到 & ":L" & 找到).值
            结果表.范围("A" & i & ":C" & i).值 = 数据表.范围("B" & j & ":D" & j).值
            i = i + 1
        结束如果
    下一 j
    
    结束子

复制列格式

    子 复制列格式()
        Dim 源范围 As 范围
        Dim 目标范围 As 范围
        Dim 源列 As 范围
        Dim 目标列 As 范围
        Dim i As 长整型
        
        设置 源范围 = 这工作簿.工作表("Sheet2").范围("C1:L1")
        设置 目标范围 = 这工作簿.工作表("Sheet3").范围("D1:M1")
        
        对于 i = 1 到 源范围.列数
            设置 源列 = 源范围.列(i)
            设置 目标列 = 目标范围.列(i)
            源列.复制
            目标列.粘贴特殊 粘贴:=xlPasteFormats
        下一 i
        
        设置 源范围 = 这工作簿.工作表("Sheet1").范围("B1:D1")
        设置 目标范围 = 这工作簿.工作表("Sheet3").范围("A1:C1")
    
        对于 i = 1 到 源范围.列数
            设置 源列 = 源范围.列(i)
            设置 目标列 = 目标范围.列(i)
            源列.复制
            目标列.粘贴特殊 粘贴:=xlPasteFormats
            应用程序.剪切复制模式 = 假
        下一 i
    结束子

希望这对您有所帮助。如果您需要进一步的帮助,请随时提出。

英文:

try

Sub find()
Dim dataSheet As Worksheet, lookupSheet As Worksheet, resultSheet As Worksheet
Dim dataRange As Range, lookupRange As Range
Dim dataArray As Variant, lookupArray As Variant
Dim i As Long, j As Long, lastRowData As Long, lastRowLookup As Long, found As Long

Set dataSheet = ThisWorkbook.Worksheets(&quot;Sheet1&quot;)
Set lookupSheet = ThisWorkbook.Worksheets(&quot;Sheet2&quot;)
Set resultSheet = ThisWorkbook.Worksheets(&quot;Sheet3&quot;)

With dataSheet
    lastRowData = .Cells(.Rows.Count, &quot;B&quot;).End(xlUp).Row
    Set dataRange = .Range(&quot;B1:B&quot; &amp; lastRowData)
    dataArray = dataRange.Value
End With

With lookupSheet
    lastRowLookup = .Cells(.Rows.Count, &quot;B&quot;).End(xlUp).Row
    Set lookupRange = .Range(&quot;B1:B&quot; &amp; lastRowLookup)
    lookupArray = lookupRange.Value
End With

i = 1

For j = 1 To UBound(dataArray, 1)
    found = Application.Match(dataArray(j, 1), lookupArray, 0)
    If Not IsError(found) Then
        resultSheet.Range(&quot;D&quot; &amp; i &amp; &quot;:M&quot; &amp; i).Value = lookupSheet.Range(&quot;C&quot; &amp; found &amp; &quot;:L&quot; &amp; found).Value
        resultSheet.Range(&quot;A&quot; &amp; i &amp; &quot;:C&quot; &amp; i).Value = dataSheet.Range(&quot;B&quot; &amp; j &amp; &quot;:D&quot; &amp; j).Value
        i = i + 1
    End If
Next j

End Sub

to copy column format

Sub CopyColumnFormats()
    Dim srcRange As Range
    Dim destRange As Range
    Dim srcCol As Range
    Dim destCol As Range
    Dim i As Long
    
    Set srcRange = ThisWorkbook.Worksheets(&quot;Sheet2&quot;).Range(&quot;C1:L1&quot;)
    Set destRange = ThisWorkbook.Worksheets(&quot;Sheet3&quot;).Range(&quot;D1:M1&quot;)
    
    For i = 1 To srcRange.Columns.Count
        Set srcCol = srcRange.Columns(i)
        Set destCol = destRange.Columns(i)
        srcCol.Copy
        destCol.PasteSpecial Paste:=xlPasteFormats
    Next i
    
    Set srcRange = ThisWorkbook.Worksheets(&quot;Sheet1&quot;).Range(&quot;B1:D1&quot;)
    Set destRange = ThisWorkbook.Worksheets(&quot;Sheet3&quot;).Range(&quot;A1:C1&quot;)

    For i = 1 To srcRange.Columns.Count
        Set srcCol = srcRange.Columns(i)
        Set destCol = destRange.Columns(i)
        srcCol.Copy
        destCol.PasteSpecial Paste:=xlPasteFormats
        Application.CutCopyMode = False
    Next i
End Sub

答案2

得分: 0

以下是VBA代码的中文翻译:

' VBA查找
'
' [![输入图像描述][1]][1]
'
'&lt;!-- 语言:lang-vb --&gt;
'
Sub 查找数据()

    Const LKP_COLUMN As Long = 1
    Const LKP_LEFT_COLUMNS_TO_EXCLUDE As Long = 1
    Const SRC_COLUMN As Long = 1
    
    ' 你的代码...
    
    ' 这两行仅用于使此代码编译。
    Const LastRowSheet2 As Long = 3 ' 查找
    Const LastRowSheet1 As Long = 4 ' 源
    
    Dim lrg As Range: Set lrg = Sheet2.Range("B1:L" & LastRowSheet2) ' 3-12
    Dim srg As Range: Set srg = Sheet1.Range("B1:D" & LastRowSheet1) ' 2-4
    Dim dfCell As Range: Set dfCell = Sheet3.Range("A2")
    
    ' 将查找范围的值写入数组。
    
    Dim lrCount As Long: lrCount = lrg.Rows.Count
    Dim lcCount As Long: lcCount = lrg.Columns.Count
    Dim lData(): lData = lrg.Value
    
    ' 将唯一值('键')及其对应的行索引('项目')写入字典。
    
    Dim lDict As Object: Set lDict = CreateObject("Scripting.Dictionary")
    lDict.CompareMode = vbTextCompare
    
    Dim lr As Long, lStr As String
    
    For lr = 1 To lrCount
        lStr = CStr(lData(lr, LKP_COLUMN))
        If Len(lStr) > 0 Then
            If Not lDict.Exists(lStr) Then
                lDict(lStr) = lr
            End If
        End If
    Next lr
    
    ' 源数据也是...
     
    Dim srCount As Long: srCount = srg.Rows.Count
    Dim scCount As Long: scCount = srg.Columns.Count
    
    ' ...目标的左侧部分,因此将其写入数组
    ' 并调整数组大小以容纳右侧的查找数据。
    
    Dim Data(): Data = srg.Value
    Dim dcCount As Long:
    dcCount = srCount + lrCount - LKP_LEFT_COLUMNS_TO_EXCLUDE
    ReDim Preserve Data(1 To srCount, 1 To dcCount)
    
    ' 遍历源数组并将匹配行写入目标数组的顶部。
    
    Dim sr As Long, dr As Long, c As Long, sStr As String
    
    For sr = 1 To srCount
        sStr = Data(sr, SRC_COLUMN)
        If lDict.Exists(sStr) Then ' 找到匹配项
            lr = lDict(sStr) ' 检索查找行
            dr = dr + 1
            ' 写入源。
            For c = 1 To scCount
                Data(sr, c) = Data(sr, c)
            Next c
            ' 写入查找。
            For c = 1 + LKP_LEFT_COLUMNS_TO_EXCLUDE To lcCount
                Data(sr, c + scCount - LKP_LEFT_COLUMNS_TO_EXCLUDE) _
                    = lData(lr, c)
            Next c
        End If
    Next sr
    
    ' 从数组顶部将结果写入范围。
   
    Dim drg As Range: Set drg = dfCell.Resize(dr, dcCount)
    drg.Value = Data
    
    ' 清除下方。
    drg.Resize(drg.Worksheet.Rows.Count - drg.Row - dr + 1).Offset(dr).Clear
    
    ' 通知。
    
    MsgBox "查找完成。", vbInformation

End Sub

注意:上述翻译只包括VBA代码部分,不包括图像描述或其他内容。

英文:

A VBA Lookup

在VBA中查找一个列中元素的最快方法

<!-- language: lang-vb -->

Sub LookupData()
Const LKP_COLUMN As Long = 1
Const LKP_LEFT_COLUMNS_TO_EXCLUDE As Long = 1
Const SRC_COLUMN As Long = 1
&#39; Your code...
&#39; These two lines are just for this code to compile.
Const LastRowSheet2 As Long = 3 &#39; Lookup
Const LastRowSheet1 As Long = 4 &#39; Source
Dim lrg As Range: Set lrg = Sheet2.Range(&quot;B1:L&quot; &amp; LastRowSheet2) &#39; 3-12
Dim srg As Range: Set srg = Sheet1.Range(&quot;B1:D&quot; &amp; LastRowSheet1) &#39; 2-4
Dim dfCell As Range: Set dfCell = Sheet3.Range(&quot;A2&quot;)
&#39; Write the values from the lookup range to an array.
Dim lrCount As Long: lrCount = lrg.Rows.Count
Dim lcCount As Long: lcCount = lrg.Columns.Count
Dim lData(): lData = lrg.Value
&#39; Write the unique values (&#39;keys&#39;) and their correcesponding
&#39; row indexes (&#39;items&#39;) to a dictionary.
Dim lDict As Object: Set lDict = CreateObject(&quot;Scripting.Dictionary&quot;)
lDict.CompareMode = vbTextCompare
Dim lr As Long, lStr As String
For lr = 1 To lrCount
lStr = CStr(lData(lr, LKP_COLUMN))
If Len(lStr) &gt; 0 Then
If Not lDict.Exists(lStr) Then
lDict(lStr) = lr
End If
End If
Next lr
&#39; The source data is also...
Dim srCount As Long: srCount = srg.Rows.Count
Dim scCount As Long: scCount = srg.Columns.Count
&#39; ... the left part of the destination so write it to an array
&#39; and resize the array to accommodate the lookup data on the right.
Dim Data(): Data = srg.Value
Dim dcCount As Long:
dcCount = srCount + lrCount - LKP_LEFT_COLUMNS_TO_EXCLUDE
ReDim Preserve Data(1 To srCount, 1 To dcCount)
&#39; Loop through the source array and write the matching rows
&#39; to the top of the destination array.
Dim sr As Long, dr As Long, c As Long, sStr As String
For sr = 1 To srCount
sStr = Data(sr, SRC_COLUMN)
If lDict.Exists(sStr) Then &#39; match found
lr = lDict(sStr) &#39; retrieve the lookup row
dr = dr + 1
&#39; Write source.
For c = 1 To scCount
Data(sr, c) = Data(sr, c)
Next c
&#39; Write lookup.
For c = 1 + LKP_LEFT_COLUMNS_TO_EXCLUDE To lcCount
Data(sr, c + scCount - LKP_LEFT_COLUMNS_TO_EXCLUDE) _
= lData(lr, c)
Next c
End If
Next sr
&#39; Write the result from the top of the array to the range.
Dim drg As Range: Set drg = dfCell.Resize(dr, dcCount)
drg.Value = Data
&#39; Clear below.
drg.Resize(drg.Worksheet.Rows.Count - drg.Row - dr + 1).Offset(dr).Clear
&#39; Inform.
MsgBox &quot;Lookup is done.&quot;, vbInformation
End Sub

huangapple
  • 本文由 发表于 2023年5月25日 20:16:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/76332180.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定