티스토리 뷰

반응형

문서를 보면 Dictionary 의 subscript 로 세개가 구현되어있다. 

 

subscript(Key) -> Value?  // get set 

subscript(Key, default _: () -> Value) -> Value // get set 

subscript(Dictionary<Key, Value>.Index) -> Dictionary<Key, Value>.Element  // get

 

3번째처럼 key-based subscript 가 아니라 index-based subscript 도 있는 줄 몰랐는데,

정리해두자! 

 

[1] subscript(Key) -> Value?

- 문서

- 코드 구현 

 

var responseMessages = [200: "OK",
                      403: "Access forbidden",
                      404: "File not found",
                      500: "Internal server error"]

// get
print(responseMessages[200]) // Optional("OK")
print(responseMessages[700]) // nil


// set
responseMessages[200] = (responseMessages[200] ?? "OK") + "✅"
responseMessages[401] = (responseMessages[401] ?? "Unauthorized") + "❌"

print(responseMessages) // [200: "OK✅", 401: "Unauthorized❌" ...]

 

 

[2] subscript(Key, default _: () -> Value) -> Value

 

문서

코드구현 


key 가 존재하지 않으면 default value를 준다. 

Accesses the value with the given key, falling back to the given default value if the key isn’t found.

 

var responseMessages = [200: "OK",
                      403: "Access forbidden",
                      404: "File not found",
                      500: "Internal server error"]


// get
print(responseMessages[200, default: "Unknown"]) // OK
print(responseMessages[700, default: "Unknown"]) // Unknown


// set
responseMessages[200, default: "OK"] += "✅"
responseMessages[401, default: "Unauthorized"] += "❌"

print(responseMessages) // [200: "OK✅", 401: "Unauthorized❌" ...]

 

실예제로 numberOfElements in Array (또는 Occurrences in Array) 를 구하는 데 편하게 쓸 수 있다. 

이렇게 extension을 만들어서 사용했던 적이 있는데, 

extension Array where Element: Hashable {

    func numberOfElements() -> [Element: Int] {
        var dic: [Element: Int] = [:]
        for element in self {
            dic[element, default: 0] += 1
        }
        return dic
    }
}

let array = [10, 20, 10, 20, 30, 20]
array.numberOfElements() // [10: 2, 20: 3, 30: 1]

 

문서에도 비슷한 예제 (Use subscript while counting the occurrences of each letter in a string) 가 있다. 

let message = "Hello, Elle!"
var letterCounts: [Character: Int] = [:]
for letter in message {
    letterCounts[letter, default: 0] += 1
}
// letterCounts == ["H": 1, "e": 2, "l": 4, "o": 1, ...]

 

 

 

[3] subscript(Dictionary<Key, Value>.Index) -> Dictionary<Key, Value>.Element

- 문서

- 코드구현 

 

index (Int가 아니라 Dictinary.Index 타입) 를 받아서 key-value pair (tuple) 를 리턴해준다.

 

var responseMessages = [200: "OK",
                      403: "Access forbidden",
                      404: "File not found",
                      500: "Internal server error"]

if let index = responseMessages.firstIndex(where: { $0.value == "OK" }) {
    print(responseMessages[index]) // (key: 200, value: "OK")
}

 

 

문서에 있는 실예제라고 할 수 있는 코드도 첨부한다. 

아래와 같이 collection-based operations 을 수행할 때, 이 subscript을 사용하면 좋다고 한다. 

let countryCodes = ["BR": "Brazil", "GH": "Ghana", "JP": "Japan"]
if let index = countryCodes.firstIndex(where: { $0.value == "Japan" }) {
    print(countryCodes[index])
    print("Japan's country code is '\(countryCodes[index].key)'.")
} else {
    print("Didn't find 'Japan' as a value in the dictionary.")
}
// Prints "(key: "JP", value: "Japan")"
// Prints "Japan's country code is 'JP'."

 

여기서 헷갈리면 안되는 점은 dic 이기때문에 array 의 firstIndex와 다르다는 점이다.

똑같은 value를 가진 key, value pair 를 추가하고 돌려봤을 때 출력값이 달라지는 것을 확인할 수 있다. 

var responseMessages = [200: "OK",
                     ✅ 201: "OK",
                      403: "Access forbidden",
                      404: "File not found",
                      500: "Internal server error"]

if let index = responseMessages.firstIndex(where: { $0.value == "OK" }) {
    print(responseMessages[index]) // (key: 201, value: "OK")
}

추측컨데,,, 명목상(?) firstIndex 로 네이밍을 한 것 같다. 

lastIndex 는 없고 코드구현을 봐도 OrderedDictionary 가 사용되지 않았기 때문이다. 

 

만약 value가 아니라 key값으로 index 를 찾는다면

명확하게 index(forKey:) 를 사용하면 좋을 것 같다는 생각이 든다. 

 

 

[3.1] KeyValuePairs

위에서 다룬 것처럼 key-value pairs 의 순서는 예측 불가능하다. 

만약 ordered collection이 필요하고 딕셔너리 처럼 fast key lookup 이 필요하지 않다면 

 

KeyValuePairs 를 대안으로 사용할 수 있다. (참고: 주석)

 

Swift-collections에서 OrderedDictionary 를 import 하지 않고

좀더 라이트하게 순서가 보장된 key value collection을 쓸  수 있는 방법이다. 

 

 

위의 예제와 달리 KeyValuePairs 를 쓰면 항상 같은 결과가 나온다.  

var responseMessages: KeyValuePairs = [200: "OK",
                      201: "OK",
                      403: "Access forbidden",
                      404: "File not found",
                      500: "Internal server error"]

if let index = responseMessages.firstIndex(where: { $0.value == "OK"}) {
    print(responseMessages[index]) // (key: 200, value: "OK")
}

 

 

몇몇 연산들은 dictionary 를 사용하는 것보다 더 속도가 느리다. 

예를들어 firstIndex(where:) 를 콜할 때는 collection 전체를 순회한다고 한다. 

코드구현을 보면 배열 기반으로 되어있다. 

 

 

+

 KeyValuePairs 는 아래와 같은 특성 때문에 혼란이 좀 있을 수 있을 것 같다.. 

 

- Key가 Hashable 이 아니여도 됨.

- duplicates keys 도 허용함.

 

이건 dic이라고 생각안하고 아주 간단히 ordered dic 같은게(?) 필요할 때 써야할 것 같다. 

 

 

 

반응형

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

[Swift] self in a closure in a closure  (0) 2023.07.11
[Swift Collections] Heap  (0) 2023.05.20
[Swift] actor  (0) 2023.04.10
[Swift] Sendable  (0) 2023.04.08
[Swift] withTaskGroup, withThrowingTaskGroup  (0) 2023.03.17
댓글