코테를 풀다가 딕셔너리를 사용해서 푸는 문제인데... 왜?? 딕셔너리로 풀어야 하는지 이해가 되지 않았다!! 왜냐!! 그냥 딕셔너리에 대해 몰랐으니깐..
그래서..!
딕셔너리에 대해서 알아보자!! 🔥
Swift의 컬렉션 타입 3가지 배열(Array), 세트(set), 딕셔너리(Dictionary)가 있는데 그중에 하나이다.
컬렉션 타입은 사실 이름만 들어도 모음?? 수집?? 무리?? 이러한 느낌이 떠오르지 않나?? 난 그랬다 ㅋㅋ Swift에서 컬렉션 타입은 지정된 타입의 데이터들의 묶음이다.
딕셔너리는 Key : Value가 함께 저장되는 자료구조이다. 그리고 정렬되지 않는 컬렉션이라는 점이 있는데 Array처럼 순서가 지정되어 있는 줄 알고 사용했다가 print를 할 때마다 뒤죽박죽 나와서 코드를 잘못 짠 줄 알았다.
var dict: [String:Int] = ["Fist" : 1, "Second" : 2, "Third" : 3]
print(dict) // ["Fist": 1, "Third": 3, "Second": 2]
당연히 계속 print를 실행해 보면 dict의 순서가 그대로 나오는 경우가 있지만!!! 딕셔너리는 순서가 지정되지 않는 것을 알 수가 있다.
그리고 Value 즉 값은 중복이 가능하지만 Key는 중복이 불가능하다. Swift는 형식에 민감하기 때문에 모든 Key의 자료형은 같아야 하고, 모든 Value의 자료형도 같아야 한다.
딕셔너리에서 사용하는 메서드를 알아보자!!! "개발자 소들이" 님의 정리가 내가 이해하기가 너무 쉬워서 그것을 바탕으로 작성을 해볼 거다!
💡 딕셔너리 생성
타입 추론으로 생성하기
var dict1 = ["height" : 180, "age" : 25]
var dict2 = [:] // Empty collection literal requires an explicit type
Empty collection literal requires an explicit type 빈 컬렉션 리터럴에는 명시적 유형이 필요합니다 ~라는 말이다!! 즉 타입 추론으로는 딕셔너리 생성이 불가능하다!!
그래서 타입 Annotation으로 생성을 한다.
var dict1: [String : Int] = [:]
이렇게 타입을 직접 명시해 주면 된다. 그럼 오류가 없이 생성된다.
생성자로 생성하기 이 방법은 안 쓸 거 같지만
var dict1 = Dictionary<String, Int>()
var dict2 = [String : Int]()
이렇게 생성할 수도 있다!
딕셔너리는 선언과 동시에 타입을 꼭 명시해줘야 하고 해당 타입으로 된 값만 딕셔너리에 저장할 수 있다.
💡 딕셔너리 개수 확인하기
var dict1: [String : Int] = ["height" : 180, "age" : 25]
let count: Int = dict1.count // 딕셔너리 갯수 확인 : 2
let isEmpty: Bool = dict.isEmpty // 딕셔너리 비었는지 확인 : false
💡 딕셔너리 요소에 접근하기
var dict1: [String : Int] = ["height" : 180, "age" : 25]
let height = dict1["height"] // Optional(180)
let weight = dict1["weight"] // nil
Subscript로 요소에 접근할 수 있는데 반환값을 보면 Optional(180)인 것을 알 수 있다. 딕셔너리는 Subscript로 요소에 접근하면 기본 반환값이 Optional Type이다.
만약 Optional Type으로 반환하기 싫다면 기본값을 액세스 해주어야 한다.
var dict1: [String : Int] = ["height" : 180, "age" : 25]
let height = dict1["height", default: 0] // 180
let age = dict1["age"] ?? 0 // 25
이런 식으로 두 가지 방법으로 할 수 있고! 그러면 반환값이 Non-Optional Type이다.
💡 딕셔너리에 요소 추가하기
우선 Subscript로 추가할 수 있다.
var dict1: [String : Int] = ["height" : 180, "age" : 25]
dict1["height"] = 100 // 해당 Key가 있다면, Value 덮어쓰기 (update)
dict1["weight"] = 60 // 해당 Key가 없다면, 추가 (insert)
print(dict1) // ["height": 100, "weight": 60, "age": 25]
Subscript의 경우, insert인지 update인지 알 수 없다!!
updateValue(:forKey)로 추가를 해보면
dict1.updateValue(100, forKey: "height") // 해당 Key가 있다면, Value 덮어쓰고 덮어쓰기 전 값 리턴 (update)
dict1.updateValue(60, forKey: "weight") // 해당 Key가 없다면, 추가하고 nil 리턴 (insert)
리턴값을 통해 insert인지 update인지 알 수 있다. 위 코드를 보면 해당 Key가 있다면 덮어쓰기 전 값을 리턴 받는데 그 말은 아!! 딕셔너리에 값이 존재하는구나! 그러면 update이네 ~~라고 알 수 있고!! 해당 Key가 없다면 nil값을 리턴하는데 아!! 존재하지 않았구나 ~~라고 알 수 있다는 말이다.
💡 딕셔너리에 요소 삭제하기
Subscript로 삭제하기 (nil 대입하기)
var dict1: [String : Int] = ["height" : 180, "age" : 25]
dict1["weight"] = nil // 해당 Key가 없어도 오류 안남
dict1["height"] = nil // 해당 Key가 있다면, 해당 Key-Value 삭제
print(dict1) // ["age": 25]
removeValue(forkey:)로 삭제하기
var dict1: [String : Int] = ["height" : 180, "age" : 25]
dict1.removeValue(forKey: "weight") // 해당 Key가 없다면, nil 반환
dict1.removeValue(forKey: "height") // 해당 Key가 있다면, 해당 Key-Value 삭제 후 삭제된 Value 반환 : Optional(100)
removeAll()로 전체 삭제하기
var dict1: [String : Int] = ["height" : 180, "age" : 25]
dict1.removeAll() // 전체 삭제하기
print(dict1) // [:]
💡 Key-Value 나열하기
var dict1: [String : Int] = ["height" : 180, "age" : 25]
// Key 모두 나열하기
dict1.keys // "height", "age"
dict1.keys.sorted() // "age", "height"
// Value 모두 나열하기
dict1.values // 180, 25
dict1.values.sorted() // 25, 100
keys, values는 찍을 때마다 다르게 나온다. 왜냐?? 딕셔너리는 정렬되지 않은 컬렉션이기 때문이다!! 맨 처음에 봤죠??
sorted를 간단하게 알아보자면 ~ sorted는 원본 배열은 그대로 두고, 사본을 만들어 이를 오름차순으로 정렬한 후 리턴해주는 것이다.
💡 딕셔너리 비교하기
var dict1: [String : Int] = ["height": 180, "age" : 25]
var dict2: [String : Int] = ["height": 180, "age" : 25]
var dict3: [String : Int] = ["Height": 180, "Age" : 25]
var dict4: [String : String] = ["name": "ykr", "address" : "Korea"]
print(dict1 == dict2) // True
print(dict1 == dict3) // false (대소문자 다름)
print(dict1 == dict4) // false (Binary operator '==' cannot be applied to operands of type '[String : Int]' and '[String : String]')
비교 연산자로 비교할 수 있지만, 모든 Key와 Value가 정확히 모두 일치해야만 True가 된다!!
💡 딕셔너리 요소 검색하기
var dict1: [String : Int] = ["height": 180, "age" : 25]
let condition: ((String, Int)) -> Bool = {
$0.0.contains("h")
}
// 1. contains(where:) : 해당 클로저를 만족하는 요소가 하나라도 있을 경우 true
dict1.contains(where: condition) // true
// 2. first(where:) : 해당 클로저를 만족하는 첫 번쨰 요소 튜플로 리턴 (딕셔너리는 순서가 없기 때문에, 호출할 때마다 값이 바뀔 수 있음)
dict1.first(where: condition) // Optional((key: "height", value: 180))
// 3. filter : 해당 클로저를 만족하는 요소만 모아서 새 딕셔너리로 리턴
dict1.filter(condition) // ["height": 180]
딕셔너리 요소를 검색할 때는 클로저를 이용해서 검색한다. 이때!! 클로저의 파라미터 타입은 내가 지정한 딕셔너리의 타입을 갖는 튜플이어야 한다!! 그리고 반환 타입은 무조건 Bool이어야 한다!
참고자료
'Swift' 카테고리의 다른 글
| [Swift] 배열 정렬 sort와 sorted (3) | 2024.02.18 |
|---|---|
| [Swift] forEach(_:) 알아보기 (2) | 2024.02.17 |
| [Swift] 삼항연산자 (2) | 2023.12.29 |
| [Swift] map{ Int($0)! } vs map{ Int(String($0))! } (2) | 2023.11.18 |
| [Swift] Array.count에서 오류가 난 이유? (3) | 2023.11.16 |