ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Swift - Dictionary
    Programming/Swift 2021. 10. 8. 15:09

    안녕하세요 Beepeach입니다 :)

    오늘 공부해볼 내용은 Dictionary입니다.

    Dictionary는 Array와 더불어 자주 사용하는 Collection 중에 하나입니다.

    그리고 가장 큰 특징은 element가 Key와 Value로 구성되어 있다는 점입니다.

     

     

    Dictionary는 hash-table의 일종입니다. 그래서 저장된 element를 매우 빠르게 접근이 가능합니다.

    (Hash table은Key에 Value를 저장하는  자료구조 중 하나입니다. 자세한 것은 Data Structure 포스팅에서 다루도록 하겠습니다.)

     

    table의 각 항목들은 hashable 타입인 key를 이용하여 정의됩니다.

    Key는 주로 문자열이나 숫자를 사용합니다.

     

    그리고 이 key에 대응하는 value를 저장하고 접근할 수 있습니다.

    Value는 hashable 타입이 아니여도 됩니다. 어떠한 object가 와도 괜찮습니다.

     

    주의할 점은 Key는 중복되면 안됩니다.

    Dictionary는 key를 이용하여 value에 접근하므로 key가 중복이 된다면 그중에 어떤 value에 접근해야 하는지 모르겠죠??

     

    그리고 array와는 다르게 정렬이 되어있지 않습니다.

    정렬이 되어있지 않은 특성 때문에 dictionary를 서로 비교할 때는 주의해야 합니다.

     

    그럼 CRUD 순으로 공부해보도록 하겠습니다.

     


    Create

     

    Dictionary를 생성하는 방법은 Dictionary Literal을 사용해서 생성할 수 있습니다.

     

     

    Dictionary Literal은 배열과 같이 []로 생성합니다. 다만 차이점은 :(comma)로 key와 value를 구분해줍니다.

    ["B": "Beepeach", "P": "Peach"]과 같이 작성하면 key와 value의 타입이 String인 dictionary가 생성됩니다.

    여기서 주의해야 할 점은 element, 즉 요소의 개수는 2개라는 점입니다.

    "B"를 key로 가지고 "Beepeach"를 value로 가지는 element 1개

    "P"를 key로 가지고 "Peach"를 value로 가지는 element 1개 이렇게 총 2개의 element를 가집니다.

    4개라고 착각하시면 안 됩니다!

     

    Type을 확인해보면 Dictionary<String, String> 타입이라고 나옵니다.

    배열과 같이 딕셔너리도 정식 문법과 단축 문법이 존재합니다.

    Generic을 이용한 정식 문법은 Dictionary<String, Int>와 같이 나타냅니다.

    주의할 점은 key와 valye의 구분이 , 로 되어있다는 점입니다. 이러한 이유는 Generic의 type 열거방법이 ,로 구분되기 때문입니다.

    (Generic은 이후에 공부해보도록 할게요.)

     

    정식 문법은 잘 사용하지 않고 대부분 단축 문법을 이용합니다.

    Dictionary<String, Int>와 같은 단축 문법은 [String: Int] 입니다.

     

    예제를 확인해보면 빈 딕셔너리를 생성하는 여러 방법을 보여주고 있습니다.

    첫 번째는 단축 문법으로 type annotation을 하고 [:] 빈 dictionary literal을 할당했습니다.

    두 번째는 정식 문법과 생성자와 type inference를 이용해 빈 딕셔너리를 생성했습니다.

    마지막으로 단축 문법과 생성자, type inference를 이용해 빈 딕셔너릴 생성했습니다.

     

    여러 가지 방법 중 어느 것을 사용해도 상관없습니다.

    하지만 제 포스팅에서는 단축 문법을 이용한 방법을 주로 사용하겠습니다.

     


    Read

     

    Dictionary는 Collection이므로 배열에서 사용했던 isEmpty, count 속성들을 그대로 사용할 수 있습니다.

    그래서 요소의 개수, 딕셔너리가 빈 딕셔너리인지 아닌지 쉽게 확인할 수 있습니다.

     

    Dictionary의 요소에 접근하기 위해서는 []와 key를 이용하여 접근할 수 있습니다.

    words["A"]를 한다면 만약 "A" key가 존재한다면 value를 리턴해줍니다.

    존재하지 않는다면 nil을 리턴합니다. 

     

    그런데 이 말은 어디서 많이 들어본 거 같습니다! 존재하면 값을 리턴하고 존재하지 않으면 nil을 리턴한다??

    Optional에서 많이 들어본 말이죠??? 맞습니다!

    Subscript와 Key를 이용해 접근한 값의 타입은 Optional입니다. 위에서 직접 확인해보니 Optional <String>인 것을 확인할 수 있습니다.

    생각해보면 당연한 일입니다. 

    Key에 해당하는 값은 없을 수 있으니까요! 배열의 index와는 다르게 crash가 발생하는 것이 아닌 optional로 처리함으로써 안전하게 사용할 수 있습니다.

     

    이때 subscript문법에서  default를 사용할 수 있습니다.

    이 의미는 default값을 사용해서 만약 key에 해당하는 value가 없다면 default값을 사용하겠다!라는 말입니다.

    그럼 절대 nil을 리턴할 경우가 생기지 않겠죠??? 그래서 default를 사용하면 type이 optional이 아닙니다.

     

    Dictionary는 정렬되지 않은 Collection입니다.

    정렬되지 않았지만 Collection이죠?? 그러므로 for-in 문에서 반복이 가능합니다.

    Dictionary를 key와 value의 tuple로 decomposing(분해)가 가능합니다.

     

    그리고 Dictionary에서 제공하는 keys, vlaues옵션을 통해서 key와 value만 따로 반복할 수도 있습니다.

     

     

    keys를 확인해보면 key만을 포함하는 Collection이라고 되어 있습니다.

    Collection이므로 for-in 반복문에서 반복이 가능하겠죠??

    예제에서 확인해보면 반복 가능한 것을 확인할 수 있습니다.

    중요한 점은 keys와 values도 정렬되어 있지 않습니다. 그래서 print를 해보면 매번 값이 달라지는 것을 확인할 수 있습니다.

    정렬하여 사용하고 싶다면 sorted() 메서드를 이용하면 됩니다.

     

    그리고 타입을 확인해보면 구조체로 되어있습니다.

    아마 Dictionary 내부에 선언된 구조체인 거 같습니다.

    그래서 keys가 array라고 생각하시면 안 됩니다.

     

     

    직접 확인을 해보니 array에 할당이 되지 않습니다.

    만약 Array로 만들고 싶다면 예제와 같이 Array() 생성자로 생성해주면 간단하게 Array로 생성할 수 있습니다.

     

     


    Update

     

    Dictionary를 수정하는 방법은 subscript를 그대로 이용하면 됩니다.

    주의할 점부터 먼저 보겠습니다.

     

    Read에서 봤던 default를 사용하여 value를 가져오는 코드에서 key에 맞는 value가 없다면 default값을 리턴해줬습니다.

    그럼 이 key와 value가 저장이 됐을까요??? 확인해보면 되겠죠??

    확인을 해봤더니 dictionary에는 변화가 없는 것을 확인할 수 있습니다.

    이 방법은 update를 하는 방법이 아닙니다.

     

    그럼 추가하는 방법을 보겠습니다.

    빈 dictionary를 생성하고 subscript를 이용하여 key와 value를 할당해주면 끝입니다.

    간단하죠??

     

    만약 이미 존재하는 key에 다른 value를 할당하면 어떻게 될까요??

    그러면 이전에 저장되어있던  value는 사라지고 새로운 value로 교체가 됩니다.

    이러한 작동방식은 update + insert = upsert라고 합니다. (크게 중요하지는 않습니다..)

     

    Dictionary가 제공하는 메서드를 이용하는 방법도 있습니다.

    updateValue(_:forKey) 메서드로 value와 key를 전달하면 upsert기능을 똑같이 수행할 수 있습니다.

    그럼 왜 이 메서드를 사용하는 걸까요??

     

     

    Upsert 하면 upsert인거지 리턴 값이 있습니다.

    심지어 그 리턴값이 optional이네요???

     

     

    리턴 값은 만약 존재하지 않았던 key라면 nil을 리턴하고 존재하던 key라면 이전에 저장되어있던 값을 리턴해줍니다.

    만약 교체되는 값이 필요하다면 이 메서드를 사용하면 됩니다.

     

     

     

     


    Delete

     

    삭제를 시킬 때도 subscript를 이용합니다.

    Subscript와 key를 이용한 리턴 값은 optional이라고 했습니다.

    그러므로 nil을 저장하면 삭제와 같은 기능을 할 수 있습니다.

     

    그럼 궁금한 점이 하나 생깁니다.

    원래부터 없던 key에 nil을 저장하면 어떻게 될까???

    존재하지 않는 key에 nil을 저장해도 crash가 발생하지 않는 것을 확인할 수 있습니다.

     


    Subscript를 이용한 방법 말고 메서드를 이용하는 방법도 있습니다.

    removeValue(forKey:) 메서드를 이용하면 쉽게 삭제가 가능합니다.

    배열에서 사용했던 remove(at:)과 비슷하게 삭제시키면 삭제시킨 Value를 리턴해줍니다.

     

    하지만 다른 부분은 리턴 값이 optional이므로 존재하지 않는 key를 통해 삭제를 하더라도 crash가 발생하지 않습니다.

     

    removeAll() 메서드는 이전에 배운 메서드와 동일한 메서드입니다.

     


    Compare

     

    Dictionary를 전체적으로 비교하는 것은 쉽습니다.

    기존에 사용하던 비교 연산자를 이용하면 같은 dictionary인지 아닌지 쉽게 판단할 수 있습니다.

    Dictionary는 정렬되어 있지 않기 때문에 순서는 상관이 없습니다.

    저장된 요소가 모두 같다면 true, 하나라도 다르다면 false입니다.

    대소 문자도 비교하기때문에 대소문자도 다르다면 false입니다.

     

    Dictionary에서 기본적으로 제공해주는 메서드를 이용해도 됩니다.

     

     

    Array에서 사용했었던 메서드입니다.

    하지만 대소문자를 무시하고 비교하거나 특별한 비교를 하지 않는 이상 이 메서드를 사용하는 것은 추천하지 않습니다.

    이유는 메서드 설명에 있습니다.

     

    Same order .. 같은 정렬일 때 비교를 하게 됩니다.

    Dictionary는 정렬되어있지 않다고 했습니다. 그럼 그냥 이 메서드를 사용하면 어떤 일이 벌어질까요??

     

     

    이 계산의 결과는 매번 달라집니다.

    True일 때도 있고 false일 때도 있습니다.

     

    왜 그럴까요???? 이유는 dictionary는 정렬되어 있지 않아서입니다.

    print를 통해서 매번 비교하는 값을 살펴보면 element 한 개씩 차례대로 비교하는 것을 확인할 수 있습니다.

    그래서 운이 좋아 모든 정렬순서가 똑같아 같은 것을 비교하게 되면 true를 리턴하고 아니면 false를 리턴하게 되는 것입니다.

     

    그럼 대소문자를 무시하고 비교하고 싶은데 어떻게 해야 할까요?? 방법이 없는 걸까요??

    해결방법은 간단합니다.

    비교대상을 정렬해주면 해결이 됩니다.

     

     

    Key를 keys.sorted()로 정렬을 해주고 이를 통해 value를 얻으면 key도 정렬이 되어있고 value도 정렬이 되어있습니다.

    정렬된 key와 value를 이용하면 결과는 항상 동일합니다.

     


    Search

     

     

    검색도 Dictionary에서 기본으로 제공하는 메서드를 이용하면 쉽습니다.

    자주 사용하는  세 가지 메서드를 살펴보겠습니다.

    주의해서 봐야 할 부분은 이 메서드들은 모두 closure를 파라미터로 받습니다.

    그리고 각각 리턴해주는 값도 모두 다릅니다.

     

     

    contians(where:) 메서드는 파라미터로 받은 closure를 모두 만족한다면 true를 리턴합니다.

    리턴 값은 true 혹은 false입니다.

     

    first(where:) 메서드는 closure를 만족하는 첫 번째 element를 리턴합니다.

    그럼 dictionary는 정렬되어있지 않으니까 closure를 만족하는 element가 여러 개면 실행할 때마다 결과가 달라지겠죠??

    리턴 값은 key와 value로 이루어진 tuple을 리턴해줍니다.

     

     

    filter(_:) 메서드는 closure를 만족하는 element만을 모아서 새로운 dictionary로 리턴해줍니다.

    리턴값은 dictionary입니다.

    간단히 살펴보았으니 예시도 간단하게 보겠습니다...?

    파라미터에 closure를 직접 전달해도 되지만 이번에는  closure를 미리 선언해놓고 사용해보겠습니다.

     

     

    Closure를 살펴보면 key와 value는 모두 String형식이고 key가 "B"이거나 value에 "e"를 포함하면 true를 리턴하도록 했습니다.

    (혹시 Closure를 잘 모르신다면 이 포스팅을 참고해주세요!)

     

    다음에 알아볼 부분은 크게 중요한 부분은 아니니 넘어가셔도 좋습니다.

    그냥 공부하다가 신기해서 적어봅니다.

     


    Dictionary Index??

     

     

    Dictionary도 collection이죠?? 그럼 index가 존재한다는 뜻입니다.

    근데 dictionary는 정렬이 안되어있잖아요?? 그런데도 index를 사용해서 접근이 가능한가?라는 의문이 들었습니다.

     

     

    넵 당연히 error가 발생합니다.

    아무래도 array처럼 int index로는 접근이 안되는 거 같습니다.

    그런데 dictionary가 제공하는 method에는 다음과 같은 method가 있습니다.

     

     

    이름에도 Index가 들어가고 리턴 값도 Index를 리턴하고 있습니다!

    그럼 이 메서드를 이용해서 index를 얻고 index를 이용해서 key와 value에 접근해보겠습니다.

     

     

    key가 "A"인 element의 index를 바인딩시켜서 이를 통해서 key와 value에 접근했습니다.

    여기서 한 가지 더 신기한 것은 Index로 접근하면 optional이 아니라는 점입니다.

    (firstIndex(where:)로 얻은 Index는 optional입니다. 이 index를 가지고 접근한 element가 optional이 아니란 소리입니다.)

    그래서 key와 value에 접근할 때 옵셔널체이닝을 사용하지 않고 접근한 것을 볼 수 있습니다.

     

    네! 결론이 뭐냐구요???

    그냥 Dictionary도 index가 있구나.. 입니다.

     

     


     

    참고자료

     

    https://kxcoding.com

     

    여러분의 새로운 도전을 응원합니다 | KxCoding

    Mastering SwiftUI 더 적은 코드로, 더 멋진 UI 만들기

    kxcoding.com

     

     

    https://docs.swift.org/swift-book/LanguageGuide/CollectionTypes.html

     

    Collection Types — The Swift Programming Language (Swift 5.6)

    Collection Types Swift provides three primary collection types, known as arrays, sets, and dictionaries, for storing collections of values. Arrays are ordered collections of values. Sets are unordered collections of unique values. Dictionaries are unordere

    docs.swift.org

     

     

    https://developer.apple.com/documentation/swift/dictionary

     

    Apple Developer Documentation

     

    developer.apple.com

     

     

    728x90

    'Programming > Swift' 카테고리의 다른 글

    Swift - KeyValuePairs  (0) 2021.10.14
    Swift - Set  (0) 2021.10.14
    Swift - Array  (0) 2021.08.30
    Swift - Foundation Collection & Swift Collection  (0) 2021.08.28
    Swift - Sequence와 Collection (Sequence와 Collection의 차이)  (2) 2021.08.11
Designed by Tistory.