在VBA中设置数据结构

时间:2017-01-18 13:08:02

标签: excel vba excel-vba data-structures

我正在寻找在Excel VBA中使用的set数据结构。到目前为止我发现的是Scripting.Dictionary,它似乎是map

在VBA中还有类似的设置吗?

基本上我正在寻找一种有效的数据结构,以确定是否已经添加了特定值。

3 个答案:

答案 0 :(得分:4)

看看.NET ArrayList,它有AddContainsSort等方法。您可以在VBS和VBA环境中实例化对象:

Set ArrayList = CreateObject("System.Collections.ArrayList")

Scripting.Dictionary也可以满足需求,它有唯一的密钥,Exists方法允许检查密钥是否已经在字典中。

但是,通过ADODB的SQL请求可能会更有效。以下示例显示如何通过SQL查询将唯一行检索到工作表:

Option Explicit

Sub GetDistinctRecords()

    Dim strConnection As String
    Dim strQuery As String
    Dim objConnection As Object
    Dim objRecordSet As Object

    Select Case LCase(Mid(ThisWorkbook.Name, InStrRev(ThisWorkbook.Name, ".")))
        Case ".xls"
            strConnection = "Provider=Microsoft.Jet.OLEDB.4.0;User ID=Admin;Data Source='" & ThisWorkbook.FullName & "';Mode=Read;Extended Properties=""Excel 8.0;HDR=YES;"";"
        Case ".xlsm", ".xlsb"
            strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;User ID=Admin;Data Source='" & ThisWorkbook.FullName & "';Mode=Read;Extended Properties=""Excel 12.0 Macro;HDR=YES;"";"
    End Select

    strQuery = "SELECT DISTINCT * FROM [Sheet1$]"
    Set objConnection = CreateObject("ADODB.Connection")
    objConnection.Open strConnection
    Set objRecordSet = objConnection.Execute(strQuery)
    RecordSetToWorksheet Sheets(2), objRecordSet
    objConnection.Close

End Sub

Sub RecordSetToWorksheet(objSheet As Worksheet, objRecordSet As Object)

    Dim i As Long

    With objSheet
        .Cells.Delete
        For i = 1 To objRecordSet.Fields.Count
            .Cells(1, i).Value = objRecordSet.Fields(i - 1).Name
        Next
        .Cells(2, 1).CopyFromRecordset objRecordSet
        .Cells.Columns.AutoFit
    End With

End Sub

源数据应放在Sheet1上,结果输出到Sheet2。该方法的唯一限制是ADODB连接到驱动器上的Excel工作簿,因此在查询之前应保存所有更改以获得实际结果。

如果您只想获得一组非不同的行,那么查询应如下所示(只是一个示例,您必须将您的字段集放入查询中):

    strQuery = "SELECT CustomerID, CustomerName, ContactName, Address, City, PostalCode, Country FROM [Sheet1$] GROUP BY CustomerID, CustomerName, ContactName, Address, City, PostalCode, Country HAVING Count(*) > 1"

答案 1 :(得分:2)

您可以使用集合并执行以下功能,集合强制使用唯一的密钥标识符:

Public Function InCollection(Col As Collection, key As String) As Boolean
  Dim var As Variant
  Dim errNumber As Long

  InCollection = False
  Set var = Nothing

  Err.clear
  On Error Resume Next
    var = Col.Item(key)
    errNumber = CLng(Err.Number)
  On Error GoTo 0

  '5 is not in, 0 and 438 represent incollection
  If errNumber = 5 Then ' it is 5 if not in collection
    InCollection = False
  Else
    InCollection = True
  End If

End Function

答案 2 :(得分:2)

只需编写Scripting.Dictionary的包装器,它只公开类似集合的操作。

<强> clsSet

Option Explicit

Private d As Scripting.Dictionary

Private Sub Class_Initialize()
    Set d = New Scripting.Dictionary
End Sub

Public Sub Add(var As Variant)
    d.Add var, 0
End Sub

Public Function Exists(var As Variant) As Boolean
    Exists = d.Exists(var)
End Function

Public Sub Remove(var As Variant)
    d.Remove var
End Sub

然后你可以这样使用它:

<强> mdlMain

Public Sub Main()
    Dim s As clsSet
    Set s = New clsSet

    Dim obj As Object

    s.Add "A"
    s.Add 3
    s.Add #1/19/2017#

    Debug.Print s.Exists("A")
    Debug.Print s.Exists("B")
    s.Remove #1/19/2017#
    Debug.Print s.Exists(#1/19/2017#)
End Sub

按预期打印True,False和False。