본문 바로가기

Swift/기초 문법

컬렉션 타입 3편 - 딕셔너리

  • 딕셔너리 (dictionary) 는 순서와 상관없이 콜렉션에 같은 타입의 키 (key)와 같은 타입의 값 (value)를 저장합니다.
  • 각 값은 딕셔너리 내부에서 값에 대한 식별자로 동작하는 유니크한 키 와 조합됩니다.
  • 배열의 아이템과 다르게 딕셔너리의 아이템은 특정 순서를 가지고 있지 않습니다.
  • 특정 단어를 찾기위해 사전을 찾는 방법과 같이 식별자를 기준을 값을 찾을 때 딕셔너리를 사용합니다.

 

딕셔너리 타입 짧은 구문 (Dictionary Type Shorthand Syntax)

  • Swift 딕셔너리의 타입은 Dictionary<Key, Value> 로 적으며 Key 는 딕셔너리 키로 사용되는 값의 타입이고 Value 는 딕셔너리에 저장될 값의 타입 입니다.
  • 딕셔너리 Key 타입 집합의 값 타입과 같이 반드시 Hashable 프로토콜을 준수해야 합니다.
  • [Key: Value] 와 같이 짧은 형식으로 딕셔너리 타입을 작성할 수도 있습니다. 두 형식은 기능적으로 동일하지만 이 가이드에서는 짧은 형식을 더 선호합니다.

 

빈 딕셔너리 생성 (Creating an Empty Dictionary)

  • 배열 처럼 초기화 구문을 사용하여 타입을 포함한 빈 Dictionary 를 생성할 수 있습니다:
var namesOfIntegers = [Int: String]()
// namesOfIntegers is an empty [Int: String] dictionary
  • 이 예제에서 정수 값에 사람이 읽을 수 있는 이름을 저장하는 [Int: String] 타입의 빈 딕셔너리를 생성합니다.
  • 키는 Int 타입이고 값은 String 타입 입니다.
  • 컨텍스트가 이미 타입 정보를 제공한다면 [:] (대괄호 안에 콜론을 포함해야 합니다)와 같이 작성하여 빈 딕셔너리 리터럴로 빈 딕셔너리를 생성할 수 있습니다:
namesOfIntegers[16] = "sixteen"
// namesOfIntegers now contains 1 key-value pair
namesOfIntegers = [:]
// namesOfIntegers is once again an empty dictionary of type [Int: String]

 

딕셔너리 리터럴로 딕셔너리 생성 (Creating a Dictionary with a Dictionary Literal)

  • 앞서 배열 리터럴과 비슷한 구문을 가진 딕셔너리 리터럴 (dictionary literal) 로 딕셔너리를 초기화 할 수 있습니다.
  • 딕셔너리 리터럴은 Dictionary 콜렉션으로 하나 이상의 키-값 쌍으로 작성하는 짧은 작성법 입니다.
  • 키-값 쌍 은 키와 값의 결합입니다.
  • 딕셔너리 리터럴에서 각 키-값 쌍의 키와 값은 콜론으로 구분됩니다. 키-값 쌍은 콤마로 구분하고 대괄호로 둘러싸 리스트 형식으로 작성됩니다
  • 아래 예제는 국제 공항의 이름을 저장하는 딕셔너리를 생성합니다.
  • 딕셔너리에 키는 3글자로 된 국제 항공 운송 협회 (Air Transport Association) 코드이고 값은 공항 이름입니다:
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
  • airports 딕셔너리는 Dictionary 키는 String 타입이고 값은 String 타입인 [String: String] 타입을 갖도록 선언됩니다.
  • airports 딕셔너리는 2개의 키-값 쌍을 포함한 딕셔너리 리터럴로 초기화 됩니다.
  • 첫번째 쌍은 "YYZ" 의 키와 "Toronto Pearson" 값을 가집니다.
  • 두번째 쌍은 "DUB" 의 키와 "Dublin" 값을 가집니다.
  • 딕셔너리 리터럴은 2개의 String: String 쌍을 포함합니다.
  • 키-값 타입은 airports 변수 선언 (딕셔너리는 String 키와 String값만 가능) 타입과 동일하며 airports 딕셔너리에 2개의 초기 아이템을 포함하여 초기화 하는 것은 가능합니다.
  • 배열과 마찬가지로 키와 값이 일관된 타입을 갖는 딕셔너리 리터럴로 초기화하는 경우 딕셔너리 타입을 작성할 필요가 없습니다.
  • airports 의 초기화는 짧은 표현으로 작성 가능합니다:
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
  • 리터럴 안에 모든 키와 값이 각각 같은 타입이기 때문에 Swift는 airports 딕셔너리를 사용하기 위해 [String: String] 이 올바른 타입이라는 것을 추론할 수 있습니다.

 

딕셔너리 접근과 수정 (Accessing and Modifying a Dictionary)

  • 메서드와 프로퍼티 또는 서브 스크립트 구분을 사용하여 딕셔너리에 접근과 수정이 가능합니다.
  • 배열과 마찬가지로 읽기 전용 count 프로퍼티Dictionary 에 아이템의 갯수를 확인할 수 있습니다:
print("The airports dictionary contains \\(airports.count) items.")
// Prints "The airports dictionary contains 2 items."
  • count 프로퍼티가 0 과 같은지 아닌지 부울 isEmpty 프로퍼티를 이용하여 확인할 수 있습니다:
if airports.isEmpty {
    print("The airports dictionary is empty.")
} else {
    print("The airports dictionary is not empty.")
}
// Prints "The airports dictionary is not empty."
  • 서브 스크립트 구문을 사용하여 딕셔너리에 새로운 아이템을 추가할 수 있습니다.
var exampleDict = ["A": 1, "B": 2]
exampleDict["C"] = 3 // Add a new key-value pair
print(exampleDict) // ["A": 1, "B": 2, "C": 3]
  • 적절한 키의 타입을 서브 스크립트 인덱스로 사용하고 적절한 값의 타입을 할당 합니다:
airports["LHR"] = "London"
// the airports dictionary now contains 3 items
  • 특정 키를 서브 스크립트 구문으로 사용하여 값을 변경할 수 있습니다:
airports["LHR"] = "London Heathrow"
// the value for "LHR" has been changed to "London Heathrow"
  • 서브 스크립트 외에 딕셔너리의 updateValue(_:forKey:) 메서드를 사용하여 특정 키에 값을 설정하거나 업데이트 할 수 있습니다.
  • 위의 서브 스크립트 예제와 같이 updateValue(_:forKey:) 메서드는 해당 키에 값이 존재하지 않으면 값을 설정 하거나 해당 키에 값이 존재하면 값을 업데이트 합니다.
  • 그러나 서브 스크립트와 다르게 updateValue(_:forKey:) 메서드는 업데이트 수행 후에 이전 값을 반환합니다. 이를 통해 업데이트가 발생했는지 여부를 알 수 있습니다.
  • updateValue(_:forKey:) 메서드는 딕셔너리의 값 타입의 옵셔널 값을 반환합니다.
  • 예를 들어 딕셔너리에 String 값을 저장하면 String? 타입 또는 "옵셔널 String"을 메서드는 반환합니다.
  • 이 옵셔널 값은 해당 키에 존재한 업데이트 전의 값 또는 존재한 값이 없었을 때는 nil 을 포함합니다:
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
    print("The old value for DUB was \\(oldValue).")
}
// Prints "The old value for DUB was Dublin."
  • 또한 딕셔너리에 removeValue(forKey:) 메서드를 사용하여 키-값 쌍을 삭제할 수 있습니다.
  • 이 메서드는 키-값 쌍이 존재하면 삭제하고 삭제된 값을 반환하거나 값이 존재하지 않으면 nil 을 반환합니다:
if let removedValue = airports.removeValue(forKey: "DUB") {
    print("The removed airport's name is \\(removedValue).")
} else {
    print("The airports dictionary does not contain a value for DUB.")
}
// Prints "The removed airport's name is Dublin Airport."

 

딕셔너리 반복 (Iterating Over a Dictionary)

  • for-in 루프로 딕셔너리에 키-값 쌍을 반복할 수 있습니다.
  • 딕셔너리의 각 아이템은 (key, value) 튜플로 반환되고 튜플의 멤버를 임시 상수 또는 변수로 분리할 수 있습니다:
for (airportCode, airportName) in airports {
    print("\\(airportCode): \\(airportName)")
}
// LHR: London Heathrow
// YYZ: Toronto Pearson
  • 딕셔너리의 keys 와 values 프로퍼티로 딕셔너리의 키 또는 값에 반복 가능한 콜렉션을 가져올 수도 있습니다:
for airportCode in airports.keys {
    print("Airport code: \\(airportCode)")
}
// Airport code: LHR
// Airport code: YYZ

for airportName in airports.values {
    print("Airport name: \\(airportName)")
}
// Airport name: London Heathrow
// Airport name: Toronto Pearson
  • 딕셔너리의 키 또는 값을 Array 인스턴스의 API를 사용해야 할 경우 keys 또는 values 프로퍼티로 새로운 배열을 초기화 하십시오:
let airportCodes = [String](airports.keys)
// airportCodes is ["LHR", "YYZ"]

let airportNames = [String](airports.values)
// airportNames is ["London Heathrow", "Toronto Pearson"]
  • Swift의 Dictionary 타입은 정의된 순서를 가지고 있지 않습니다.
  • 특정 순서로 딕셔너리의 키 또는 값을 반복하려면 keys 또는 values 프로퍼티에 sorted() 메서드를 사용 하십시오.

 

불변 딕셔너리와 가변 딕셔너리

  • let으로 선언된 딕셔너리는 불변이고 수정이 불가능하며, var로 선언된 딕셔너리는 수정 가능합니다. 
let constantDict = ["A": 1, "B": 2]
// constantDict["A"] = 3 // Error: Cannot modify a constant dictionary

var mutableDict = ["A": 1, "B": 2]
mutableDict["A"] = 3 // 성공

 

중첩된 딕셔너리

  • 딕셔너리는 다른 딕셔너리, 배열 등 중첩된 자료구조를 저장할 수 있습니다.
  • 중첩된 딕셔너리 접근 시에도 서브스크립트를 활용합니다. 
var nestedDict: [String: [String: String]] = [
    "Person1": ["Name": "Alice", "City": "New York"],
    "Person2": ["Name": "Bob", "City": "San Francisco"]
]
nestedDict["Person1"]?["Name"] = "Alicia"
print(nestedDict)

 

Dictionary의 키 타입 제약

  • Key 타입은 반드시 Hashable 프로토콜을 준수해야 합니다.
  • 사용자 정의 타입을 키로 사용하려면 Hashable 프로토콜을 구현해야 합니다. 
struct Person: Hashable {
    let id: Int
    let name: String
}

var personDictionary: [Person: String] = [
    Person(id: 1, name: "Alice"): "Engineer",
    Person(id: 2, name: "Bob"): "Designer"
]

 

기본 값 사용

  • 딕셔너리에서 특정 키의 값이 없을 경우 기본값을 제공할 수 있습니다.
  • default 매개변수를 활용하여 간편히 처리합니다: 
let airports = ["YYZ": "Toronto Pearson", "LHR": "London Heathrow"]
let defaultValue = airports["DUB", default: "Unknown Airport"]
print(defaultValue) // "Unknown Airport"

 

중복된 키 처리

  • 딕셔너리 리터럴에서 동일한 키가 여러 번 나타나면 마지막 값이 저장됩니다. 
let duplicateKeysDict = ["A": 1, "B": 2, "A": 3]
print(duplicateKeysDict) // ["B": 2, "A": 3]

 

정렬된 딕셔너리

  • 딕셔너리의 기본 데이터 구조는 순서를 보장하지 않으므로 정렬된 딕셔너리를 생성하려면 별도의 배열로 정렬 후 활용해야 합니다. 
let sortedKeys = airports.keys.sorted()
for key in sortedKeys {
    print("\(key): \(airports[key]!)")
}

 

compactMapValues 활용

  • 값을 변환하거나 필터링할 때 유용합니다. 
let scores = ["Alice": "95", "Bob": "fail", "Charlie": "87"]
let validScores = scores.compactMapValues { Int($0) }
print(validScores) // ["Alice": 95, "Charlie": 87]

'Swift > 기초 문법' 카테고리의 다른 글

컬렉션 타입 2편 - 집합  (0) 2024.12.11
컬렉션 타입 1편 - 배열  (0) 2024.12.10
enum 케이스 나누기  (0) 2024.12.02
기본 연산자 (Basic Operators)  (0) 2024.12.02