vba 2列的快速比较方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19567060/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Fast compare method of 2 columns
提问by Clemens
EDIT:Instead for my solution, use something like
编辑:而不是我的解决方案,使用类似
For i = 1 To tmpRngSrcMax
If rngSrc(i) <> rngDes(i) Then ...
Next i
It is about 100 times faster.
它大约快 100 倍。
I have to compare two columns containing string data using VBA. This is my approach:
我必须使用 VBA 比较包含字符串数据的两列。这是我的方法:
Set rngDes = wsDes.Range("A2:A" & wsDes.Cells(Rows.Count, 1).End(xlUp).Row)
Set rngSrc = wsSrc.Range("I3:I" & wsSrc.Cells(Rows.Count, 1).End(xlUp).Row)
tmpRngSrcMax = wsSrc.Cells(Rows.Count, 1).End(xlUp).Row
cntNewItems = 0
For Each x In rngSrc
tmpFound = Application.WorksheetFunction.CountIf(rngDes, x.Row)
Application.StatusBar = "Processed: " & x.Row & " of " & tmpRngSrcMax & " / " & Format(x.Row / tmpRngSrcMax, "Percent")
DoEvents ' keeps Excel away from the "Not responding" state
If tmpFound = 0 Then ' new item
cntNewItems = cntNewItems + 1
tmpLastRow = wsDes.Cells(Rows.Count, 1).End(xlUp).Row + 1 ' first empty row on target sheet
wsDes.Cells(tmpLastRow, 1) = wsSrc.Cells(x.Row, 9)
End If
Next x
So, I'm using a For Each loop to iterate trough the 1st (src) column, and the CountIf method to check if the item is already present in the 2nd (des) column. If not, copy to the end of the 1st (src) column.
因此,我使用 For Each 循环遍历第 1 (src) 列,并使用 CountIf 方法检查该项目是否已存在于第 2 (des) 列中。如果没有,请复制到第一 (src) 列的末尾。
The code works, but on my machine it takes ~200s given columns with around 7000 rows. I noticed that CountIf works way faster when used directly as a formula.
该代码有效,但在我的机器上,给定大约 7000 行的列需要大约 200 秒。我注意到 CountIf 在直接用作公式时工作得更快。
Does anyone has ideas for code optimization?
有没有人有代码优化的想法?
采纳答案by Clemens
Ok. Let's clarify a few things.
好的。让我们澄清一些事情。
So column A
has 10,000
randomly generated values , column I
has 5000
randomly generated values. It looks like this
所以列A
有10,000
随机生成的值,列I
有5000
随机生成的值。看起来像这样
I have run 3 different codes against 10,000 cells.
我对 10,000 个单元格运行了 3 种不同的代码。
the for i = 1 to ... for j = 1 to ...
approach, the one you are suggesting
的for i = 1 to ... for j = 1 to ...
方法,你所提出的建议之一
Sub ForLoop()
Application.ScreenUpdating = False
Dim stNow As Date
stNow = Now
Dim lastA As Long
lastA = Range("A" & Rows.Count).End(xlUp).Row
Dim lastB As Long
lastB = Range("I" & Rows.Count).End(xlUp).Row
Dim match As Boolean
Dim i As Long, j As Long
Dim r1 As Range, r2 As Range
For i = 2 To lastA
Set r1 = Range("A" & i)
match = False
For j = 3 To lastB
Set r2 = Range("I" & j)
If r1 = r2 Then
match = True
End If
Next j
If Not match Then
Range("I" & Range("I" & Rows.Count).End(xlUp).Row + 1) = r1
End If
Next i
Debug.Print DateDiff("s", stNow, Now)
Application.ScreenUpdating = True
End Sub
Sid's appraoch
席德的做法
Sub Sample()
Dim wsDes As Worksheet, wsSrc As Worksheet
Dim rngDes As Range, rngSrc As Range
Dim DesLRow As Long, SrcLRow As Long
Dim i As Long, j As Long, n As Long
Dim DesArray, SrcArray, TempAr() As String
Dim boolFound As Boolean
Set wsDes = ThisWorkbook.Sheets("Sheet1")
Set wsSrc = ThisWorkbook.Sheets("Sheet2")
DesLRow = wsDes.Cells(Rows.Count, 1).End(xlUp).Row
SrcLRow = wsSrc.Cells(Rows.Count, 1).End(xlUp).Row
Set rngDes = wsDes.Range("A2:A" & DesLRow)
Set rngSrc = wsSrc.Range("I3:I" & SrcLRow)
DesArray = rngDes.Value
SrcArray = rngSrc.Value
For i = LBound(SrcArray) To UBound(SrcArray)
For j = LBound(DesArray) To UBound(DesArray)
If SrcArray(i, 1) = DesArray(j, 1) Then
boolFound = True
Exit For
End If
Next j
If boolFound = False Then
ReDim Preserve TempAr(n)
TempAr(n) = SrcArray(i, 1)
n = n + 1
Else
boolFound = False
End If
Next i
wsDes.Cells(DesLRow + 1, 1).Resize(UBound(TempAr) + 1, 1).Value = _
Application.Transpose(TempAr)
End Sub
my (mehow) approach
我的(mehow)方法
Sub Main()
Application.ScreenUpdating = False
Dim stNow As Date
stNow = Now
Dim arr As Variant
arr = Range("A3:A" & Range("A" & Rows.Count).End(xlUp).Row).Value
Dim varr As Variant
varr = Range("I3:I" & Range("I" & Rows.Count).End(xlUp).Row).Value
Dim x, y, match As Boolean
For Each x In arr
match = False
For Each y In varr
If x = y Then match = True
Next y
If Not match Then
Range("I" & Range("I" & Rows.Count).End(xlUp).Row + 1) = x
End If
Next
Debug.Print DateDiff("s", stNow, Now)
Application.ScreenUpdating = True
End Sub
the results as follows
结果如下
now, you select the fast compare method:)
现在,您选择快速比较方法:)
filling in of the random values
填充随机值
Sub FillRandom()
Cells.ClearContents
Range("A1") = "Column A"
Range("I2") = "Column I"
Dim i As Long
For i = 2 To 10002
Range("A" & i) = Int((10002 - 2 + 1) * Rnd + 2)
If i < 5000 Then
Range("I" & Range("I" & Rows.Count).End(xlUp).Row + 1) = _
Int((10002 - 2 + 1) * Rnd + 2)
End If
Next i
End Sub
回答by Reafidy
Here is non-looping code that executes almost instantly for the example given above from mehow.
下面是非循环代码,对于上面从 mehow 给出的示例,几乎立即执行。
Sub HTH()
Application.ScreenUpdating = False
With Range("A2", Cells(Rows.Count, "A").End(xlUp)).Offset(, 1)
.Formula = "=VLOOKUP(A2,I:I,1,FALSE)"
.Value = .Value
.SpecialCells(xlCellTypeConstants, 16).Offset(, -1).Copy Range("I" & Rows.Count).End(xlUp).Offset(1)
.ClearContents
End With
Application.ScreenUpdating = True
End Sub
You can use whatever column you like as the dummy column.
您可以使用任何您喜欢的列作为虚拟列。
Info: Done get caught in the loop
信息: 完成陷入循环
Some notes on speed testing:
Compile vba project before running test.
For Each Loops execute faster than For i = 1 To 10 loops.
If possible exit the loop if the answer is found to prevent pointless loops with Exit For.
Long executes faster than integer.
关于速度测试的一些注意事项:
在运行测试之前编译 vba 项目。
For Each 循环的执行速度比 For i = 1 到 10 循环快。
如果可能的话,如果找到答案以防止使用 Exit For 进行无意义循环,则退出循环。
Long 的执行速度比 integer 快。
Finally a faster loop method (if you must loop but its still not as fast as the above non-looping method):
最后一个更快的循环方法(如果你必须循环但它仍然不如上面的非循环方法快):
Sub Looping()
Dim vLookup As Variant, vData As Variant, vOutput As Variant
Dim x, y
Dim nCount As Long
Dim bMatch As Boolean
Application.ScreenUpdating = False
vData = Range("A2", Cells(Rows.Count, "A").End(xlUp)).Value
vLookup = Range("I2", Cells(Rows.Count, "I").End(xlUp)).Value
ReDim vOutput(UBound(vData, 1), 0)
For Each x In vData
bMatch = False
For Each y In vLookup
If x = y Then
bMatch = True: Exit For
End If
Next y
If Not bMatch Then
nCount = nCount + 1: vOutput(nCount, 0) = x
End If
Next x
Range("I" & Rows.Count).End(xlUp).Offset(1).Resize(nCount).Value = vOutput
Application.ScreenUpdating = True
End Sub
As per @brettdj comments a For Next alternative:
根据@brettdj 评论 For Next 替代方案:
For x = 1 To UBound(vData, 1)
bMatch = False
For y = 1 To UBound(vLookup, 1)
If vData(x, 1) = vLookup(y, 1) Then
bMatch = True: Exit For
End If
Next y
If Not bMatch Then
nCount = nCount + 1: vOutput(nCount, 0) = vData(x, 1)
End If
Next x
回答by Cool Blue
if you use .Value2 instead of .Value it will be a little bit faster again.
如果你使用 .Value2 而不是 .Value 它会再次快一点。
回答by Siddharth Rout
Just wrote this quickly... Can you test this for me?
刚刚写的这么快……你能帮我测试一下吗?
Sub Sample()
Dim wsDes As Worksheet, wsSrc As Worksheet
Dim rngDes As Range, rngSrc As Range
Dim DesLRow As Long, SrcLRow As Long
Dim i As Long, j As Long, n As Long
Dim DesArray, SrcArray, TempAr() As String
Dim boolFound As Boolean
Set wsDes = ThisWorkbook.Sheets("Sheet1")
Set wsSrc = ThisWorkbook.Sheets("Sheet2")
DesLRow = wsDes.Cells(Rows.Count, 1).End(xlUp).Row
SrcLRow = wsSrc.Cells(Rows.Count, 1).End(xlUp).Row
Set rngDes = wsDes.Range("A2:A" & DesLRow)
Set rngSrc = wsSrc.Range("I3:I" & SrcLRow)
DesArray = rngDes.Value
SrcArray = rngSrc.Value
For i = LBound(SrcArray) To UBound(SrcArray)
For j = LBound(DesArray) To UBound(DesArray)
If SrcArray(i, 1) = DesArray(j, 1) Then
boolFound = True
Exit For
End If
Next j
If boolFound = False Then
ReDim Preserve TempAr(n)
TempAr(n) = SrcArray(i, 1)
n = n + 1
Else
boolFound = False
End If
Next i
wsDes.Cells(DesLRow + 1, 1).Resize(UBound(TempAr) + 1, 1).Value = _
Application.Transpose(TempAr)
End Sub
回答by crazynut
I just tweaked Mehow to get items missing from both list. Just in case somebody may need it. Thanks for the code sharing
我只是调整了 Mehow 以从两个列表中删除项目。以防万一有人可能需要它。感谢分享代码
Sub Main()
Application.ScreenUpdating = False
Dim stNow As Date
stNow = Now
Dim varr As Variant
varr = Range("A2:A" & Range("A" & Rows.count).End(xlUp).row).Value
Dim arr As Variant
arr = Range("I3:I" & Range("I" & Rows.count).End(xlUp).row).Value
Dim x, y, match As Boolean
For Each y In arr
match = False
For Each x In varr
If y = x Then match = True
Next x
If Not match Then
Range("B" & Range("B" & Rows.count).End(xlUp).row + 1) = y
End If
Next
Range("B1") = "Items not in A Lists"
Range("B" & Range("B" & Rows.count).End(xlUp).row + 1) = "Items not in I Lists"
'Dim arr As Variant
arr = Range("A3:A" & Range("A" & Rows.count).End(xlUp).row).Value
'Dim varr As Variant
varr = Range("I3:I" & Range("I" & Rows.count).End(xlUp).row).Value
'Dim x, y, match As Boolean
For Each x In arr
match = False
For Each y In varr
If x = y Then match = True
Next y
If Not match Then
Range("B" & Range("B" & Rows.count).End(xlUp).row + 1) = x
End If
Next
Debug.Print DateDiff("s", stNow, Now)
Application.ScreenUpdating = True
End Sub
回答by Gera
Function Ranges_Iguais(rgR1 As Range, rgR2 As Range) As Boolean
Dim vRg1 As Variant
Dim vRg2 As Variant
Dim i As Integer, j As Integer
vRg1 = rgR1.Value
vRg2 = rgR2.Value
i = 0
Do
i = i + 1
j = 0
Do
j = j + 1
Loop Until vRg1(i, j) <> vRg2(i, j) Or j = UBound(vRg1, 2)
Loop Until vRg1(i, j) <> vRg2(i, j) Or i = UBound(vRg1, 1)
Ranges_Iguais = (vRg1(i, j) = vRg2(i, j))
End Function
回答by Santosh Visal
Set R1 = Range(S1.Cells(1, 1), S1.Cells.SpecialCells(xlCellTypeLastCell))
Set R2 = Range(S2.Cells(1, 1), S2.Cells.SpecialCells(xlCellTypeLastCell))
If R1.Count = R2.Count Then
Set R3 = Range(S3.Cells(1, 1), S3.Cells(S2.Cells.SpecialCells(xlCellTypeLastCell).Row, S2.Cells.SpecialCells(xlCellTypeLastCell).Column))
R3.Formula = "=" & R1.Address(, , , True) & "=" & R2.Address(, , , True)
Set R = R3.Find(What:="FALSE", After:=S3.Cells(1, 1), LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=True, SearchFormat:=False)
bComp = R Is Nothing
Else
bComp = False
End If