VBA에는 사전 구조가 있습니까?


답변:


341

예.

MS 스크립팅 런타임 ( 'Microsoft 스크립팅 런타임')에 대한 참조를 설정하십시오. @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 스크립팅 런타임"을 참조하고 (도구-> 참조로 이동) 확인란을 선택해야합니다.
regjo

7
어, VBA 컬렉션이 열쇠입니다. 그러나 우리는에 대한 다른 정의를 가지고있을 것입니다 keyed.
David-W-Fenton

8
Excel 2010을 사용하고 있지만 "Microsoft 스크립팅 런타임"도구-참조에 대한 참조가 없습니다. CreateObject를 수행해도 작동하지 않습니다. 따라서 @masterjo 위의 의견이 잘못되었다고 생각합니다. 내가 뭔가를 잃어 버리지 않는 한
ihightower

4
참고로 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

Contains()@TWiStErRob 덕분에 2015 년 6 월 24 일 수정 : 더 짧 습니다.

2015 년 9 월 25 일 수정 : Err.Clear()@scipilot 덕분에 추가되었습니다 .


5
Add 메소드에는 선택적 "key"인수가 있으므로 내장 Collection 오브젝트를 지적하기위한 사전은 사전으로 사용할 수 있습니다.
Simon Tewsi

8
컬렉션 객체의 나쁜 점은 키가 이미 컬렉션에 있는지 확인할 수 없다는 것입니다. 오류가 발생합니다. 그건 큰 일입니다. 나는 컬렉션에 대해 좋아하지 않습니다. (나는 해결 방법이 있지만 대부분은 "못생긴"다는 것을 알고있다)
MiVoth

5
VBA Dictionary IS에서 문자열 키 (예 : c.Item ( "Key2")) 조회는 해시되었지만 정수 인덱스 (예 : c.Item (20)) 조회는 그렇지 않습니다. 이는 선형 for / next입니다. 스타일 검색 및 피해야합니다. 문자열 키 조회 또는 반복마다 컬렉션을 사용하는 것이 가장 좋습니다.
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. 여전히 재귀 컬렉션을 허용합니다. Key는 항상 필수이며 내 구현에 더 합리적이기 때문에 항상 먼저 나옵니다. 또한 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 (엑셀), 그리고 VB.NET


2
질문 하나를 더 읽을 수 있습니다. VBA에 대해 물었습니다. VB가 아니라 VB.Net이 아닌 다른 언어가 아닌 Visual Basic for Application입니다.

1
fessGUID : 그러면 다시 답을 더 읽어야합니다! 이 답변은 VBA (특히 첫 번째 링크)에도 사용할 수 있습니다.
Konrad Rudolph

5
인정합니다. 질문을 너무 빨리 읽었습니다. 그러나 나는 그가 알아야 할 것을 말해주었습니다.
Matthew Flaschen

5
@Oorang, VBA가 VB.NET의 하위 집합이 된 증거는 없습니다. 오피스의 역전 규칙-지금까지 작성된 모든 Excel 매크로를 변환하려고한다고 상상해보십시오.
Richard Gadsden

2
VBA는 실제로 VB6의 슈퍼 세트입니다. VB6과 동일한 핵심 DLL을 사용하지만 Office의 특정 응용 프로그램에 대한 모든 기능을 추가합니다.
David-W-Fenton 1

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.