본문 바로가기
VBA/엑사남_기초방

[기초방] VBA 100제 #61 [ 사진 정보 가져오기 ]

by 일등미노왕국 2023. 9. 15.

이 문제를 낸 의도는 거추장스런 For문 또는 Find문을 사용하는 것보단 함수식을 사용해서 빠르고 간편하게 사용할 수 있다는 생각에서 문제를 내었지만, 아직까진 속도에서 딕셔너리를 넘길 수 없었다. 이번 딕셔너리처럼 Value값이 배열로 담을 수 있다면 속도는 더 짧아질것이다.

 

본 문제를 쉽게 접근하게 되면 이렇게 피를 보게 된다.

 

더보기
Option Explicit

Sub 기초방61()

    Dim rngAll As Range: Set rngAll = Range([a2], [a2].End(4))
    Dim Vall, VA, vDict
    Dim T!, i&, sKey$
    Dim Vtemp()
    Dim Dict As Object: Set Dict = CreateObject("Scripting.Dictionary")
    
    rngAll.Columns("c:e").ClearContents                 '= 초기화
    
    T = Timer                                           '= 시간측정
    
    rngAll.Columns(3) = "X"                                            '= 함수구문
    rngAll.Columns(4) = "=IFERROR(INDEX(사진정보!$B$2:$B$14212,MATCH(환자정보!B2,사진정보!$A$2:$A$14212,0)),"""")"
    rngAll.Columns(5) = "=IFERROR(INDEX(사진정보!$c$2:$c$14212,MATCH(환자정보!B2,사진정보!$A$2:$A$14212,0)),"""")"
    
    
    rngAll.CurrentRegion.Copy
    rngAll.CurrentRegion.PasteSpecial xlPasteValues                     '= 값으로 붙혀넣기
    
    
    rngAll.Columns(4).SpecialCells(2, 1).Offset(0, -1) = "O"            '= 문자가 있는 곳에 O표시
    Application.CutCopyMode = False
    
    MsgBox "함수시간 : " & Format(Timer - T, "#0.00") & " 초가 소요됨"
    
    rngAll.Columns("c:e").ClearContents
    
    T = Timer
    
    Vall = Range(Sheets(2).[a2], Sheets(2).[c2].End(4))                 '= 딕셔너리 구문
    
    i = 1
    For Each VA In Application.Index(Vall, , 1)
        
        vDict = Array("O", Vall(i, 2), Vall(i, 3))
        Dict.Add Format(VA, "00000000"), vDict
        i = i + 1
    
    Next VA
    
    Vall = Range(Sheets(1).[b2], [b2].End(4))
    
    ReDim Vtemp(1 To UBound(Vall, 1), 1 To 3)
    i = 1
    For Each VA In Vall
    
        sKey = Format(VA, "00000000")
    
        If Dict.exists(sKey) Then
        
            vDict = Dict(sKey)
            Vtemp(i, 1) = vDict(0)
            Vtemp(i, 2) = vDict(1)
            Vtemp(i, 3) = vDict(2)
            
        Else
        
            Vtemp(i, 1) = "X"
            
        End If
    
        i = i + 1
    
    Next VA
    
    [c2].Resize(UBound(Vtemp, 1), 3) = Vtemp
    MsgBox "딕셔너리시간 : " & Format(Timer - T, "#0.00") & " 초가 소요됨"
    

End Sub

함수식의 코드는 단순한 index + Match 함수를 사용하여 최소한의 반복문도 피하였다.

딕셔너리의 경우 item들을 배열에 담아 딕셔너리에 추가하였다

 

중간에 

 

If Dict.exists(sKey) Then
        
    vDict = Dict(sKey)
    Vtemp(i, 1) = vDict(0)
    Vtemp(i, 2) = vDict(1)
    Vtemp(i, 3) = vDict(2)

Else

    Vtemp(i, 1) = "X"

End If

vDict = Dict(sKey)와 Dict(sKey)(0), Dict(sKey)(1), Dict(sKey)(2)의 속도차이에서 본인이 언급을 하였고, 같은 크루인 준빠님이 다음과 같이 정리를 하였다.

 

우리는 vDict에 뭐가 담겨 있는지도 알고 Dict(sKey)(0), Dict(sKey)(1), Dict(sKey)(2) 여기에 각각 뭐가 담겨있는지도 알지만 컴퓨터는 vDict 는 명확하게 뭐가 담겨있는지 알지만 Dict(sKey)(0), Dict(sKey)(1), Dict(sKey)(2)는 각각을 연산하면서 값들을 도출하게 됨으로 가급적 값들을 변수에 담아서 구문을 해결하는 것이 참 개발자의 길이다.

 

다음은 준빠님이 이러한 현상에 대해서 간단한 예시를 든 내용이다.

기초방61.xlsm
0.77MB

댓글