Excel 在VBA中获取列的所有唯一值的更快方法?

Excel 在VBA中获取列的所有唯一值的更快方法?,excel,vba,Excel,Vba,有没有更快的方法 Set data = ws.UsedRange Set unique = CreateObject("Scripting.Dictionary") On Error Resume Next For x = 1 To data.Rows.Count unique.Add data(x, some_column_number).Value, 1 Next x On Error GoTo 0 在这里,代码>唯一。键< /Cord>得到我需要的,但是对于拥有成千上万个记录

有没有更快的方法

Set data = ws.UsedRange

Set unique = CreateObject("Scripting.Dictionary")

On Error Resume Next
For x = 1 To data.Rows.Count
    unique.Add data(x, some_column_number).Value, 1
Next x
On Error GoTo 0

在这里,<>代码>唯一。键< /Cord>得到我需要的,但是对于拥有成千上万个记录的文件来说,循环本身似乎非常慢(但这在Python或C++中是一个根本不成问题的问题)。

< p>加载数组中的值会快得多:

Dim data(), dict As Object, r As Long
Set dict = CreateObject("Scripting.Dictionary")

data = ActiveSheet.UsedRange.Columns(1).Value

For r = 1 To UBound(data)
    dict(data(r, some_column_number)) = Empty
Next

data = WorksheetFunction.Transpose(dict.keys())

你也应该考虑脚本的早期绑定。字典:

Dim dict As New Scripting.Dictionary  ' requires `Microsoft Scripting Runtime` '
请注意,使用字典要比使用大型数据集快得多

作为奖励,这里有一个类似于从2D阵列中删除重复项的过程:

Public Sub RemoveDuplicates(data, ParamArray columns())
    Dim ret(), indexes(), ids(), r As Long, c As Long
    Dim dict As New Scripting.Dictionary  ' requires `Microsoft Scripting Runtime` '

    If VarType(data) And vbArray Then Else Err.Raise 5, , "Argument data is not an array"

    ReDim ids(LBound(columns) To UBound(columns))

    For r = LBound(data) To UBound(data)         ' each row '
        For c = LBound(columns) To UBound(columns)   ' each column '
            ids(c) = data(r, columns(c))                ' build id for the row
        Next
        dict(Join$(ids, ChrW(-1))) = r  ' associate the row index to the id '
    Next

    indexes = dict.Items()
    ReDim ret(LBound(data) To LBound(data) + dict.Count - 1, LBound(data, 2) To UBound(data, 2))

    For c = LBound(ret, 2) To UBound(ret, 2)  ' each column '
        For r = LBound(ret) To UBound(ret)      ' each row / unique id '
            ret(r, c) = data(indexes(r - 1), c)   ' copy the value at index '
        Next
    Next

    data = ret
End Sub
试试这个

Option Explicit

Sub UniqueValues()
Dim ws As Worksheet
Dim uniqueRng As Range
Dim myCol As Long

myCol = 5 '<== set it as per your needs
Set ws = ThisWorkbook.Worksheets("unique") '<== set it as per your needs

Set uniqueRng = GetUniqueValues(ws, myCol)

End Sub


Function GetUniqueValues(ws As Worksheet, col As Long) As Range
Dim firstRow As Long

With ws
    .Columns(col).RemoveDuplicates Columns:=Array(1), header:=xlNo

    firstRow = 1
    If IsEmpty(.Cells(1, col)) Then firstRow = .Cells(1, col).End(xlDown).row

    Set GetUniqueValues = Range(.Cells(firstRow, col), .Cells(.Rows.Count, col).End(xlUp))
End With

End Function
选项显式
子唯一值()
将ws设置为工作表
调光范围
暗霉

myCol=5'使用Excel的AdvancedFilter函数执行此操作。

使用Excel内置C++是用较小的数据集最快的方式,使用字典对于较大的数据集更快。例如:

复制A列中的值并在B列中插入唯一值:

Range("A1:A6").AdvancedFilter Action:=xlFilterCopy, CopyToRange:=Range("B1"), Unique:=True

它也适用于多个列:

Range("A1:B4").AdvancedFilter Action:=xlFilterCopy, CopyToRange:=Range("D1:E1"), Unique:=True
请注意多列,因为它并不总是按预期工作。在这些情况下,我求助于删除重复项,这是通过选择列来实现的。参考:

在这里,我将基于第三列删除重复列:

Range("A1:C4").RemoveDuplicates Columns:=3, Header:=xlNo
Range("A1:C4").RemoveDuplicates Columns:=Array(2, 3), Header:=xlNo
在这里,我删除了基于第二列和第三列的重复列:

Range("A1:C4").RemoveDuplicates Columns:=3, Header:=xlNo
Range("A1:C4").RemoveDuplicates Columns:=Array(2, 3), Header:=xlNo

PowerShell是一个非常强大和高效的工具。这有点作弊,但通过VBA炮击PowerShell会带来很多选择

下面的大部分代码只是将当前工作表保存为csv文件。输出是另一个仅具有唯一值的csv文件

Sub AnotherWay()
Dim strPath As String
Dim strPath2 As String

Application.DisplayAlerts = False
strPath = "C:\Temp\test.csv"
strPath2 = "C:\Temp\testout.csv"
ActiveWorkbook.SaveAs strPath, xlCSV
x = Shell("powershell.exe $csv = import-csv -Path """ & strPath & """ -Header A | Select-Object -Unique A | Export-Csv """ & strPath2 & """ -NoTypeInformation", 0)
Application.DisplayAlerts = True

End Sub

这很有趣,因为我不得不一遍又一遍地阅读这些说明,但它认为我找到了一种更快的方法:

Set data = ws.UsedRange
dim unique as variant
unique = WorksheetFunction.Unique(data)
然后,您可以使用
unique
数组执行任何操作,例如迭代它:

For i = LBound(unique) To UBound(unique)
    Range("Q" & i) = indexes(i, 1)
Next

您需要添加参考“Microsoft脚本运行时”,我已经添加了它。它似乎找不到“.Dictionary”中的“Scripting”似乎并不重要,即使是后期绑定,它也能在一眨眼之间运行。为什么那个代码比我的要快得多?用excel一个单元格一个单元格地读很慢。加载数组中的数据并在必要时将其写回会更快。@MGae2M,使用字典上的
.Keys()
来获取数组中的唯一值。这太棒了!2个问题#1-这是将数据粘贴到工作表中,而不是将其保存在VBA变量中#2-它查看的是公式,而不是实际值(对我来说,不是在列中粘贴唯一值,而是只将一个常用公式粘贴到一个单元格中)。@ZygD 1。范围是一个变量,您可以在VBA中使用它。2.如果做得不对,请使用“粘贴”作为值,而不是在公式1上进行粘贴。即使从技术上讲,范围是一个变量。。。但问题是,您不能使用
AdvancedFilter
方法仅将数据放入“VBA可见”变量,如数组或字典(即工作表中不存在“物理”变量)。2.我找不到怎么做,这个
AdvancedFilter
方法真的提供了粘贴为值的选项吗?问题#3-这个方法删除了工作表中的原始筛选器(如果存在)。
AdvancedFilter
不是最快的方法。在大数据集上,使用字典的性能要优于高级过滤器(对于100k单元格,大约500毫秒而不是60秒)。这是一个好方法。但是需要注意的是,这会修改原始的源列。此函数仅在Office 365中可用。我想谢谢,使用此解决方案,您可以在一个列中返回唯一值数组,只需一行代码:
unique=WorksheetFunction.unique(列(1))
这是一个强大的代码:但愿我早知道这一点:)