VBA มีโครงสร้างพจนานุกรมหรือไม่


คำตอบ:


341

ใช่.

ตั้งค่าการอ้างอิงถึง MS Scripting runtime ('Microsoft Scripting Runtime') ตามความคิดเห็นของ @ regjo ให้ไปที่เครื่องมือ -> การอ้างอิงและทำเครื่องหมายในช่องสำหรับ 'Microsoft Scripting Runtime'

หน้าต่างอ้างอิง

สร้างตัวอย่างพจนานุกรมโดยใช้รหัสด้านล่าง:

Set dict = CreateObject("Scripting.Dictionary")

หรือ

Dim dict As New Scripting.Dictionary 

ตัวอย่างการใช้งาน:

If Not dict.Exists(key) Then 
    dict.Add key, value
End If 

อย่าลืมตั้งค่าพจนานุกรมเป็นNothingเมื่อคุณใช้งานเสร็จแล้ว

Set dict = Nothing 

17
โครงสร้างข้อมูลชนิดนี้จัดทำโดยรันไทม์ของสคริปต์ไม่ใช่โดย VBA โดยทั่วไป VBA สามารถใช้โครงสร้างข้อมูลชนิดใดก็ได้ที่สามารถเข้าถึงได้ผ่านทางอินเตอร์เฟส COM
David-W-Fenton

163
เพียงเพื่อความสมบูรณ์: คุณต้องอ้างอิง "Microsoft Scripting Runtime" เพื่อการทำงาน (ไปที่เครื่องมือ -> การอ้างอิง) และทำเครื่องหมายที่ช่อง
regjo

7
มีการใส่รหัสคอลเลกชัน Uh และ VBA keyedแต่บางทีเราก็มีความหมายที่แตกต่างกันของ
David-W-Fenton

8
ฉันใช้ Excel 2010 ... แต่ไม่มีการอ้างอิงถึงเครื่องมือ "Microsoft Scripting Runtime" - การอ้างอิงการสร้าง CreateObject ไม่ทำงาน @masterjo ฉันคิดว่าความคิดเห็นของคุณด้านบนผิด นอกจากว่าฉันจะพลาดบางสิ่งไป .. จำเป็นต้องมีเครื่องมือ -> การอ้างอิง
ihightower

4
ในฐานะที่เป็น FYI คุณไม่สามารถใช้งานได้Dim dict As New Scripting.Dictionaryหากไม่มีการอ้างอิง หากไม่มีการอ้างอิงคุณต้องใช้CreateObjectวิธีการรวมภายหลังในการสร้างวัตถุนี้
David Zemens

179

VBA มีวัตถุการรวบรวม:

    Dim c As Collection
    Set c = New Collection
    c.Add "Data1", "Key1"
    c.Add "Data2", "Key2"
    c.Add "Data3", "Key3"
    'Insert data via key into cell A1
    Range("A1").Value = c.Item("Key2")

Collectionวัตถุดำเนินการค้นหาที่สำคัญโดยใช้กัญชาเพื่อให้มันได้อย่างรวดเร็ว


คุณสามารถใช้Contains()ฟังก์ชั่นเพื่อตรวจสอบว่ามีคอลเลกชันที่มีรหัส:

Public Function Contains(col As Collection, key As Variant) As Boolean
    On Error Resume Next
    col(key) ' Just try it. If it fails, Err.Number will be nonzero.
    Contains = (Err.Number = 0)
    Err.Clear
End Function

แก้ไข 24 มิถุนายน 2558 : สั้นลงContains()ด้วย @TWiStErRob

แก้ไข 25 กันยายน 2558 : เพิ่มErr.Clear()ด้วย @scipilot


5
ทำได้ดีสำหรับการชี้ให้เห็นว่าวัตถุในคอลเลกชันสามารถใช้เป็นพจนานุกรมได้เนื่องจากวิธีการเพิ่มมีอาร์กิวเมนต์ "กุญแจ" ที่เป็นตัวเลือก
Simon Tewsi

8
สิ่งที่ไม่ดีเกี่ยวกับวัตถุคอลเลกชันคือคุณไม่สามารถตรวจสอบว่ามีกุญแจอยู่ในคอลเลกชันแล้วหรือไม่ มันเพิ่งจะโยนข้อผิดพลาด นั่นเป็นเรื่องใหญ่ฉันไม่ชอบเกี่ยวกับคอลเลกชัน (ฉันรู้ว่ามีวิธีแก้ปัญหา แต่ส่วนใหญ่เป็น "น่าเกลียด")
MiVoth

5
โปรดทราบว่าการค้นหาของสตริงคีย์ (เช่น. c.Item ("Key2")) ในพจนานุกรม VBA IS แฮช แต่การค้นหาด้วยดัชนีจำนวนเต็ม (เช่น. c.Item (20)) ไม่ใช่ - มันเป็นเส้นตรงสำหรับ / ถัดไป ค้นหาสไตล์และควรหลีกเลี่ยง ที่ดีที่สุดที่จะใช้คอลเลกชันสำหรับการค้นหาคีย์สตริงเท่านั้นหรือสำหรับการทำซ้ำแต่ละครั้ง
Ben McIntyre

4
ฉันพบว่าสั้นกว่าContains: On Error Resume Next_ col(key)_Contains = (Err.Number = 0)
TWiStErRob

5
บางทีฟังก์ชั่นควรตั้งชื่อContainsKey; บางคนที่อ่านเฉพาะการร้องขออาจทำให้สับสนในการตรวจสอบว่ามีค่าเฉพาะ
jpmc26

44

VBA ไม่มีการนำพจนานุกรมไปใช้ภายใน แต่จาก VBA คุณยังสามารถใช้วัตถุพจนานุกรมจาก MS Scripting Runtime Library ได้

Dim d
Set d = CreateObject("Scripting.Dictionary")
d.Add "a", "aaa"
d.Add "b", "bbb"
d.Add "c", "ccc"

If d.Exists("c") Then
    MsgBox d("c")
End If

29

ตัวอย่างพจนานุกรมเพิ่มเติมที่มีประโยชน์สำหรับการเก็บความถี่ของการเกิดขึ้น

นอกวง:

Dim dict As New Scripting.dictionary
Dim MyVar as String

ภายในลูป:

'dictionary
If dict.Exists(MyVar) Then
    dict.Item(MyVar) = dict.Item(MyVar) + 1 'increment
Else
    dict.Item(MyVar) = 1 'set as 1st occurence
End If

วิธีตรวจสอบความถี่:

Dim i As Integer
For i = 0 To dict.Count - 1 ' lower index 0 (instead of 1)
    Debug.Print dict.Items(i) & " " & dict.Keys(i)
Next i

1
ลิงค์สำหรับการสอนเพิ่มเติมคือ: kamath.com/tutorials/tut009_dictionary.asp
John M

นี่เป็นคำตอบที่ดีมากและฉันใช้มัน อย่างไรก็ตามฉันพบว่าฉันไม่สามารถอ้างอิง dict.Items (i) หรือ dict.Keys (i) ในลูปในขณะที่คุณทำ ฉันต้องเก็บเหล่านั้น (รายการไอเท็มและรายการคีย์) ใน vars แยกกันก่อนที่จะเข้าสู่ลูปแล้วใช้ vars เหล่านั้นเพื่อรับค่าที่ฉันต้องการ กดไลค์ - allItems = companyList.Items allKeys = companyList.Keys allItems (i) ถ้าไม่ฉันจะได้รับข้อผิดพลาด: "ไม่อนุญาตให้มีการกำหนดคุณสมบัติและขั้นตอนการรับคุณสมบัติไม่ส่งคืนวัตถุ" เมื่อพยายามเข้าถึงคีย์ (i) หรือ รายการ (i) ในลูป
raddevus

10

อาคารปิดคำตอบ cjrh ของเราสามารถสร้างประกอบด้วยฟังก์ชั่นต้องไม่มีป้ายกำกับ (ฉันทำไม่ได้เช่นการใช้ฉลาก)

Public Function Contains(Col As Collection, Key As String) As Boolean
    Contains = True
    On Error Resume Next
        err.Clear
        Col (Key)
        If err.Number <> 0 Then
            Contains = False
            err.Clear
        End If
    On Error GoTo 0
End Function

สำหรับโครงการของฉันฉันเขียนชุดของฟังก์ชั่นผู้ช่วยที่จะทำให้การประพฤติมากขึ้นเช่นCollection Dictionaryมันยังอนุญาตให้เรียกใช้ซ้ำได้ คุณจะสังเกตเห็นว่าคีย์มาก่อนเสมอเพราะเป็นข้อบังคับและเหมาะสมกว่าในการนำไปใช้ของฉัน ฉันยังใช้Stringกุญแจเท่านั้น คุณสามารถเปลี่ยนกลับได้หากต้องการ

ชุด

ฉันเปลี่ยนชื่อสิ่งนี้เป็นชุดเพราะมันจะเขียนทับค่าเก่า

Private Sub cSet(ByRef Col As Collection, Key As String, Item As Variant)
    If (cHas(Col, Key)) Then Col.Remove Key
    Col.Add Array(Key, Item), Key
End Sub

ได้รับ

errสิ่งที่เป็นวัตถุเนื่องจากคุณจะผ่านวัตถุที่ใช้setและตัวแปรโดยไม่ต้อง ฉันคิดว่าคุณสามารถตรวจสอบได้ว่ามันเป็นวัตถุหรือไม่ แต่ฉันถูกกดให้เวลา

Private Function cGet(ByRef Col As Collection, Key As String) As Variant
    If Not cHas(Col, Key) Then Exit Function
    On Error Resume Next
        err.Clear
        Set cGet = Col(Key)(1)
        If err.Number = 13 Then
            err.Clear
            cGet = Col(Key)(1)
        End If
    On Error GoTo 0
    If err.Number <> 0 Then Call err.raise(err.Number, err.Source, err.Description, err.HelpFile, err.HelpContext)
End Function

มี

สาเหตุของการโพสต์นี้ ...

Public Function cHas(Col As Collection, Key As String) As Boolean
    cHas = True
    On Error Resume Next
        err.Clear
        Col (Key)
        If err.Number <> 0 Then
            cHas = False
            err.Clear
        End If
    On Error GoTo 0
End Function

ลบ

ไม่โยนหากไม่มีอยู่ แค่ทำให้แน่ใจว่ามันถูกลบ

Private Sub cRemove(ByRef Col As Collection, Key As String)
    If cHas(Col, Key) Then Col.Remove Key
End Sub

คีย์

รับกุญแจมากมาย

Private Function cKeys(ByRef Col As Collection) As String()
    Dim Initialized As Boolean
    Dim Keys() As String

    For Each Item In Col
        If Not Initialized Then
            ReDim Preserve Keys(0)
            Keys(UBound(Keys)) = Item(0)
            Initialized = True
        Else
            ReDim Preserve Keys(UBound(Keys) + 1)
            Keys(UBound(Keys)) = Item(0)
        End If
    Next Item

    cKeys = Keys
End Function

6

พจนานุกรมรันไทม์ของสคริปต์ดูเหมือนจะมีข้อบกพร่องที่สามารถทำลายการออกแบบของคุณในขั้นตอนขั้นสูง

หากค่าพจนานุกรมเป็นอาร์เรย์คุณไม่สามารถอัปเดตค่าองค์ประกอบที่มีอยู่ในอาร์เรย์ผ่านการอ้างอิงไปยังพจนานุกรม


6

ใช่. สำหรับVB6 , VBA (Excel) และVB.NET


2
คุณสามารถอ่านคำถามเพิ่มเติมได้: ฉันถามเกี่ยวกับ VBA: Visual Basic สำหรับแอปพลิเคชันไม่ใช่สำหรับ VB ไม่ใช่สำหรับ VB.Net ไม่ใช่สำหรับภาษาอื่น ๆ

1
fessGUID: จากนั้นอีกครั้งคุณควรอ่านคำตอบเพิ่มเติม! คำตอบนี้สามารถใช้กับ VBA (โดยเฉพาะลิงก์แรก)
Konrad Rudolph

5
ฉันยอมรับ. ฉันอ่านคำถามเร็วเกินไป แต่ฉันบอกเขาว่าเขาต้องการรู้อะไร
Matthew Flaschen

5
@Oorang ไม่มีหลักฐานว่า VBA กลายเป็นส่วนย่อยของ VB.NET กฎ backcompat ใน Office - ลองจินตนาการว่าพยายามแปลงแมโคร Excel ทุกตัวที่เคยเขียน
Richard Gadsden

2
VBA นั้นจริงแล้วคือ SUPERSET ของ VB6 มันใช้ DLL แกนเดียวกับ VB6 แต่เพิ่มฟังก์ชันการทำงานทุกประเภทสำหรับแอปพลิเคชันเฉพาะใน Office
David-W-Fenton

4

ถ้าด้วยเหตุผลใดก็ตามคุณไม่สามารถติดตั้งฟีเจอร์เพิ่มเติมลงใน Excel หรือไม่ต้องการคุณสามารถใช้อาร์เรย์ได้เช่นกันอย่างน้อยก็สำหรับปัญหาง่ายๆ ในฐานะที่เป็น WhatIsCapital คุณใส่ชื่อของประเทศและฟังก์ชั่นคืนทุนให้คุณ

Sub arrays()
Dim WhatIsCapital As String, Country As Array, Capital As Array, Answer As String

WhatIsCapital = "Sweden"

Country = Array("UK", "Sweden", "Germany", "France")
Capital = Array("London", "Stockholm", "Berlin", "Paris")

For i = 0 To 10
    If WhatIsCapital = Country(i) Then Answer = Capital(i)
Next i

Debug.Print Answer

End Sub

1
แนวคิดของคำตอบนี้คือเสียง แต่โค้ดตัวอย่างจะไม่ทำงานตามที่เขียนไว้ ตัวแปรแต่ละความต้องการของตัวเองDimคำหลักCountryและCapitalจำเป็นต้องได้รับการประกาศให้เป็นสายพันธุ์เนื่องจากการใช้งานของArray(), iควรจะได้รับการประกาศ (และต้องถ้าOption Explicitเป็นชุด) และเคาน์เตอร์ห่วงเป็นไปโยนออกมาจากข้อผิดพลาดที่ถูกผูกไว้ - การปลอดภัยในการ ใช้UBound(Country)สำหรับToค่า นอกจากนี้ยังอาจสังเกตได้ว่าในขณะที่Array()ฟังก์ชั่นเป็นทางลัดที่มีประโยชน์มันไม่ใช่วิธีมาตรฐานในการประกาศอาร์เรย์ใน VBA
jcb

3

คนอื่น ๆ ทั้งหมดได้กล่าวถึงการใช้งานเวอร์ชัน scripting.runtime ของคลาส Dictionary แล้ว หากคุณไม่สามารถใช้ DLL นี้คุณยังสามารถใช้เวอร์ชันนี้เพียงเพิ่มลงในรหัสของคุณ

https://github.com/VBA-tools/VBA-Dictionary/blob/master/Dictionary.cls

มันเหมือนกับรุ่นของ Microsoft

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.