我正在尝试创建一个数组,存储“工作簿 B”中 A 列中的所有值,以便我可以引用并查看单元格的值是否位于“工作簿 A”中 A 列中的该数组中。
这是我目前所掌握的该阵列的内容:
Dim StrArray() As String
Dim TotalRows As Long
Dim X As Long
Workbooks.Open Filename:="filepath", ReadOnly:=True
With Workbooks("file").Worksheets("sheet")
TotalRows = Rows(Rows.Count).End(xlUp).Row
ReDim StrArray(1 To TotalRows)
For X = 2 To TotalRows
StrArray(X) = Cells(X, 1).Value
Next X
End With
数组的这一部分工作正常,我通过在 MsgBox 中显示数组中的所有值来确认它工作正常。当我尝试在“工作簿 A”中引用此数组以检查单元格的值是否在该数组中时,问题就出现了。
这是我所得到的该代码:
For RowCounter = LastRow To 1 Step -1
If IsInArray(Range("B" & RowCounter).Value, StrArray) Then
Range("K" & RowCounter).Value = "MATCH"
End If
Next RowCounter
Workbooks("file").Close SaveChanges:=False
这是我正在使用的功能:
Public Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
Dim i
For i = LBound(arr) To UBound(arr)
If arr(i) = stringToBeFound Then
IsInArray = True
Exit Function
End If
Next i
IsInArray = False
End Function
它没有将“MATCH”值放在 K 列中。我尝试通过在 If 语句中放置一个 MsgBox 来查看它是否将值与数组匹配,从而进行故障排除,但它给了我一个永无止境的 MsgBox 循环。如果这很重要,则 K 列中当前有文本被此代码覆盖。
答案1
如果我正在解决这样的问题,我会拒绝使用数组,而选择内置的MATCH 方法:
Sub markCellsIfPresent()
Const DICTIONARY_WORKBOOK As String = "filepath"
Const DICTIONARY_WORKSHEET = "sheet"
Dim wsActive As Worksheet
Dim rValidate As Range
Dim oCell As Range
Dim wbDictionary As Workbook
Dim wsDictionary As Worksheet
Dim rDictionary As Range
Dim searchRes As Variant
Set wsActive = ActiveSheet
Set rValidate = Application.Intersect(wsActive.UsedRange, wsActive.Columns(2))
Application.ScreenUpdating = False
Set wbDictionary = Workbooks.Open(Filename:=DICTIONARY_WORKBOOK, ReadOnly:=True)
Set wsDictionary = wbDictionary.Worksheets(DICTIONARY_WORKSHEET)
Set rDictionary = Application.Intersect(wsDictionary.UsedRange, wsDictionary.Columns(1))
For Each oCell In rValidate.Cells
searchRes = Application.Match(oCell.Text, rDictionary, 0)
If Not IsError(searchRes) Then
Rem oCell in column B (2), we set mark to column K (11), so offset is 11-2=9
oCell.Offset(0, 9).value = "MATCH"
End If
Next oCell
wbDictionary.Close
Application.ScreenUpdating = True
End Sub
当然,真正的代码应该更长 - 例如,您需要检查工作簿“filepath”是否存在并打开,其中是否有名为“sheet”的工作表,其中是否有数据等等
这段代码解决了这个问题,但没有回答关于为此目的使用数组的问题。
数组代码将会更长一些,因为我们需要一个辅助程序来填充它和一个函数来搜索它。
Sub markCellsWithArray()
Const DICTIONARY_WORKBOOK As String = "filepath"
Const DICTIONARY_WORKSHEET = "sheet"
Dim wsActive As Worksheet
Dim rValidate As Range
Dim oCell As Range
Dim wbDictionary As Workbook
Dim wsDictionary As Worksheet
Dim rDictionary As Range
Dim StrArray As Variant
Set wsActive = ActiveSheet
Set rValidate = Application.Intersect(wsActive.UsedRange, wsActive.Columns(2))
Application.ScreenUpdating = False
Set wbDictionary = Workbooks.Open(Filename:=DICTIONARY_WORKBOOK, ReadOnly:=True)
Set wsDictionary = wbDictionary.Worksheets(DICTIONARY_WORKSHEET)
Set rDictionary = Application.Intersect(wsDictionary.UsedRange, wsDictionary.Columns(1))
Rem Collect values from dictionary to array (skip empty cells)
StrArray = Array()
For Each oCell In rDictionary.Cells
If Not Trim(oCell.Text) = vbNullString Then Call AddIfNeed(Trim(oCell.Text), StrArray)
Next oCell
wbDictionary.Close
Application.ScreenUpdating = True
Rem Mark cells in active sheet
For Each oCell In rValidate.Cells
If IsInArray(Trim(oCell.Text), StrArray) Then
oCell.Offset(0, 9).value = "MATCH"
End If
Next oCell
End Sub
Sub AddIfNeed(ByVal key As String, aData As Variant)
Dim l&, r&, m&, N&, i&
l = LBound(aData)
r = UBound(aData) + 1
N = r
While (l < r)
m = l + Int((r - l) / 2)
If aData(m) < key Then l = m + 1 Else r = m
Wend
If r = N Then ' Add to end of set
ReDim Preserve aData(0 To N)
aData(N) = key
ElseIf aData(r) = key Then
' Already in the set, do nothing
Else ' Insert to set in correct place
ReDim Preserve aData(0 To N)
For i = N - 1 To r Step -1
aData(i + 1) = aData(i)
Next i
aData(r) = key
End If
End Sub
Private Function IsInArray(ByVal stringToBeFound As String, aData As Variant) As Boolean
Dim l&, r&, m&, N&, i&
l = LBound(aData)
r = UBound(aData) + 1
N = r
While (l < r)
m = l + Int((r - l) / 2)
If aData(m) < stringToBeFound Then l = m + 1 Else r = m
Wend
If r = N Then ' Add to end of set
IsInArray = False
Else
IsInArray = (aData(r) = stringToBeFound) ' TRUE if found
End If
End Function
辅助代码的诀窍是使用二分查找,这比线性搜索在逐个元素地浏览未排序数组时使用。
为了在没有辅助代码的情况下实现这个技巧,你可以使用字典对象- 一切都已存在,您不必担心自己对经典算法的实现。
但是,在足够大的数据集上测试这两个过程,看看数组算法如何胜过内置的 MATCH 方法。
答案2
JohnSUN 的代码运行良好,但我也弄清楚了如何让数组路径正常工作。
Workbooks.Open Filename:="filepath", ReadOnly:=True
With Workbooks("filename").Worksheets("sheetname")
TotalRows = Rows(Rows.Count).End(xlUp).Row
ReDim StrArray(1 To TotalRows)
For X = 2 To TotalRows
StrArray(X) = Cells(X, 1).Value
Next X
End With
Workbooks("filename").Close SaveChanges:=False
ActWS.Activate
'Adds MATCH to applicable rows
For RowCounter = LastRow To 1 Step -1
If IsInArray(Range("B" & RowCounter).Value, StrArray) Then
Range("K" & RowCounter).Value = "MATCH"
End If
Next RowCounter
End If
问题是,当应用“MATCH”时,它会将其放入错误的文件中。因此,在执行代码之前,我必须重新激活我想要的文件。