Goal
- Swift의 Collection Type - Dictionary, Set를 이해한다.
들어가기에 앞서 문법작성규칙은 아래와 같이 작성합니다.
Dictionary
딕셔너리는 키(key)와 값(value)의 쌍으로 구성되는 컬렉션 타입입니다.
이름에 걸맞게 사전과 동일한 느낌을 줍니다. 네이버사전에서 인사를 검색해보면 다음의 결과가 나타납니다.
인사라는 단어를 가지고 여러 의미로 뜻을 전달하고 있는 것을 볼 수 있습니다.
여기서 인사는 key
값이 되는 것이고 단어의 뜻은 전부 value
가 된다고 볼 수 있습니다.
하나 알 수 있는 점은, key값은 유일하되, value값은 여러 가지가 나올 수 있다는 것이겠네요.
코드를 살펴보겠습니다.
선언 및 초기화
Swift에서 Dictionary는 Dictionary<자료형, 자료형>
이나 [자료형: 자료형]
으로 선언됩니다. 되도록 [자료형: 자료형]
으로 선언합시다.
1
2
3
4
5
typealias StringDictionary = [String: String] // typealias를 적극 활용해주세요!
var fishHeightDict: Dictionary<String, Float> = Dictionary<String, Float>()
var fishWeightDict: [String: Float] = [String: Float]() // 위와 똑같은 표현입니다.
var koreanDict: StringDictionary = [:] // 콜론(:)으로 빈 딕셔너리를 생성할 수 있습니다.
딕셔너리는 언급한 바와 같이, 키와 값의 쌍으로 구성됩니다. 따라서 딕셔너리에 값을 초기화 해주기 위해서 키와 값을 등록해야 합니다.
1
2
3
4
5
6
7
8
var koreanDict: [String: String] = [:] // 콜론(:)으로 빈 딕셔너리를 생성할 수 있습니다.
koreanDict["인사1"] = "마주 대하거나 헤어질 때에 예를 표함. 또는 그런 말이나 행동."
koreanDict["인사2"] = "사회적 지위가 높거나 사회적 활동이 많은 사람."
// 인사1, 인사2가 키값이 되고, 뒤에 나오는 문장은 value값이 됩니다.
var otherDict: [String: String] = ["Hong": "SeungHyun", "Lee": "SungKyu"]
// 초깃값을 주어 초기화할 수 있습니다.
값 접근
값을 접근할 때에는 변수명[키]
형태로 접근합니다. 키에 상응하는 값이 없을 수도 있으므로 Optional 형태로 반환됩니다.
1
2
3
4
5
6
7
8
9
10
var koreanDict: [String: String] = [:] // 콜론(:)으로 빈 딕셔너리를 생성할 수 있습니다.
koreanDict["인사1"] = "마주 대하거나 헤어질 때에 예를 표함. 또는 그런 말이나 행동."
koreanDict["인사2"] = "사회적 지위가 높거나 사회적 활동이 많은 사람."
print(koreanDict["인사1"]) // 인사1의 뜻을 출력합니다.
// Prints "Optional("마주 대하거나 헤어질 때에 예를 표함. 또는 그런 말이나 행동.")"
print(koreanDict["인사2"]) // 인사2의 뜻을 출력합니다.
// Prints "Optional("사회적 지위가 높거나 사회적 활동이 많은 사람.")"
값 제거
등록된 쌍을 제거하고 싶을 때가 있을겁니다.
removeValue(forKey:)
함수 원형: @discardableResult[2] mutating func removeValue(forKey key: Key
) -> Value?
해당 메서드를 사용하면 입력받은 키에 대한 딕셔너리의 (키, 값) 쌍을 제거하고 값을 반환합니다. 키에 상응하는 값이 없을 수도 있으므로 Optional 형태로 반환됩니다.
1
2
3
4
5
6
7
8
9
var nameDict: [String: String] = ["Hong": "SeungHyun", "Lee": "SungKyu"]
print(nameDict.removeValue(forKey: "Hong"))
// Prints "Optional("SeungHyun")"
print(nameDict.removeValue(forKey: "NONE"))
// Prints "nil"
print(nameDict)
// Prints "["Lee": "SungKyu"]"
딕셔너리에 있는 모든 쌍들을 제거하고 싶을 때 removeAll() 메서드를 사용합니다.
1
2
3
4
5
var nameDict: [String: String] = ["Hong": "SeungHyun", "Lee": "SungKyu"]
nameDict.removeAll()
print(nameDict)
// Prints "[:]"
Set
세트는 순서 없는 데이터 묶음으로 저장하는 컬렉션 타입입니다. 세트는 중복된 값을 저장할 수 없으며, 세트는 순서가 없습니다.
또한, 세트는 Hashable Protocol
[3]을 준수하는 Element를 저장할 수 있습니다.
Set 생성
- Set을 초기화할 때에는
Set<자료형>
으로 초기화할 수 있습니다.1
var values: Set<Int> = Set<Int>()
-
배열 생성과 비슷하게 동작시킬 수 있습니다. 다만 타입 추론 유형이 Array로 되기 때문에, 명시적으로 Set을 선언해주어야 합니다.
1 2 3 4 5
var lastNames: Set = ["Hong", "Lee", "Shin", "Kim"] // Set 타입이 지정된 변수나 상수에 배열 리터럴을 할당하기만 하면 됩니다. var numbers = Set([1, 2, 3, 4, 5]) // 배열을 묶어 Set으로 할당해줄 수 있습니다.
Set 활용
1. insert
값을 추가할 때 사용합니다.
1
2
3
4
5
var values: Set<Int> = [1, 2, 3, 4, 5]
values.insert(6)
print(values)
// Prints "[6, 3, 4, 1, 2, 5]"
// 순서가 없기 때문에 출력이 뒤죽박죽이다.
2. remove
Set 안의 값을 제거할 때 사용합니다.
1
2
3
4
var values: Set<Int> = [1, 2, 3, 4, 5]
values.remove(1)
print(values)
// Prints "[3, 5, 4, 2]"
-
전부 제거하고 싶을 때는 removeAll() 메서드를 사용합니다.
1 2 3 4
var values: Set<Int> = [1, 2, 3, 4, 5] values.removeAll() print(values) // Prints "[]"
3. count
Set 안에 요소가 몇 개가 있는지 파악할 때 사용합니다.
1
2
3
var values: Set<Int> = [1, 2, 3, 4, 5]
print(values.count)
// Prints "5"
4. isEmpty
Set이 비어있는지 유무를 판단할 때 사용합니다.
1
2
3
4
5
6
var values: Set<Int> = [1, 2, 3, 4, 5]
print(values.isEmpty)
// Prints "false"
values.removeAll()
print(values.isEmpty)
// Prints "true"
5. contains
Set 안에 특정 값이 있는지 유무를 판단할 때 사용합니다.
1
2
3
4
5
let ingredients: Set = ["cocoa beans", "sugar", "cocoa butter", "salt"]
if ingredients.contains("sugar") {
print("No thanks, too sweet.")
}
// Prints "No thanks, too sweet."
Set 연산
Set은 우리말로 집합입니다. 따라서 집합연산도 수행할 수 있습니다. 우리가 배웠던 여집합, 교집합, 합집합과 같은 연산들이죠.
1. union
합집합입니다. A∪B
와 같은 표현입니다.
합집합은, 두 집합의 전체 요소를 가지는 집합입니다.
1
2
3
4
5
let a: Set = [1, 2, 3, 4, 5]
let b: Set = [3, 4, 5, 6, 7]
let c = a.union(b)
print(c)
// Prints "[1, 2, 3, 4, 5, 6, 7]"
2. intersection
교집합을 나타냅니다. A∩B
와 같은 표현입니다.
교집합은 두 집합에서 같은 요소를 가지는 집합입니다.
1
2
3
4
5
let a: Set = [1, 2, 3, 4, 5]
let b: Set = [3, 4, 5, 6, 7]
let c = a.intersection(b)
print(c)
// Prints "[3, 4, 5]"
3. subtracting
차집합을 의미합니다. 위 사진은 A-B
의 집합입니다.
차집합 중 A-B는, 집합 A에서 B를 제외한 요소를 가지는 집합입니다.
B-A는 반대로, B에서 A를 제외한 요소를 가지는 집합이겠죠.
1
2
3
4
5
let a: Set = [1, 2, 3, 4, 5]
let b: Set = [3, 4, 5, 6, 7]
let c = a.subtracting(b)
print(c)
// Prints "[1, 2]"
4. symmetricDifference
베타적 논리합이라고 불리우기도 하고 여집합의 합이라고도 불리웁니다. A∆B
라고 표현하기도 합니다.
베타적 논리합은 교집합의 여집합으로, A와 B의 동일한 요소를 제외한 집합입니다.
1
2
3
4
5
let a: Set = [1, 2, 3, 4, 5]
let b: Set = [3, 4, 5, 6, 7]
let c = a.symmetricDifference(b)
print(c)
// Prints "[1, 2, 6, 7]"
5. isSubset(of:), isSuperset(of:)
isSuperset은 해당 집합이 파라미터로 주어진 집합의 상위 집합인지 여부를 반환합니다.
isSubset은 반대로, 해당 집합이 파라미터로 주어진 집합의 부분 집합인지 여부를 반환합니다.
위의 그림을 예시로 코드를 작성하면 다음과 같은 결과가 나타납니다.
1
2
3
4
5
6
7
8
9
let A: Set = [1, 2, 3, 4, 5]
let B: Set = [6, 7, 8, 9, 10]
let U = A.union(B) // A와 B의 합집합을 U로 둠
print(U.isSuperset(of: A))
// Prints "true", "A가 U의 상위 집합임"
print(B.isSubset(of: U))
// Prints "true", "B가 U의 부분 집합임"
6. isStrictSubset(of:), isStrictSuperset(of:)
5번 그림과 같습니다.
5번과 같은 함수처럼 보이네요. 하지만 Strict가 들어간 것으로 보아서는 무언가 제약된 집합을 얘기하는 것처럼 보입니다.
isStrictSubset과 isStrictSuperset은 주어진 집합이 해당 집합과 부분집합(또는 상위집합)이되 자기 자신과 같지 않으면 true를 리턴합니다.
즉, U라는 집합과 B라는 집합이 있을 때 (사진 참고), B의 모든 요소가 U에 있고, U는 B에 포함되어있지 않은 요소가 있다면 U는 B의 엄격한 상위집합이고, B는 U의 엄격한 부분집합이라고 할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let A: Set = [1, 2, 3, 4, 5]
let B: Set = [6, 7, 8, 9, 10]
let U: Set = A.union(B)
print(U.isStrictSuperset(of: A))
// Prints "true", "A가 U의 엄격한 상위 집합임"
print(B.isStrictSubset(of: U))
// Prints "true", "B가 U의 엄격한 부분 집합임"
print(B.isSuperset(of: B))
// Prints "true", 같은 B라도 자기자신은 부분집합이 될 수 있음
print(B.isStrictSuperset(of: B))
// Prints "false", 요소가 전부 동일하기 때문에 엄격한 상위집합이 될 수 없음
7. isDisjoint(with:)
5번, 6번 그림과 같습니다.
위 그림을 볼 때 A와 B는 서로 연관성이 하나도 없습니다. 이를 서로 배타적이라고 부르는데, 이를 isDisjoint라는 함수로 알 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
let A: Set = [1, 2, 3, 4, 5]
let B: Set = [6, 7, 8, 9, 10]
let U: Set = A.union(B)
print(A.isDisjoint(with: B))
// Prints "true", "A와 B는 배타적이다."
print(B.isDisjoint(with: A))
// Prints "true", "거꾸로 써도 결과는 동일, B와 A는 배타적이다."
print(U.isDisjoint(with: A))
// Prints "false", "U는 A의 상위집합이기 때문에, U와 A는 배타적이지 않다."
Reference
- Camel Case - 위키피디아
-
@discardableResult는 리턴값을 사용하지 않을 때 나오는 경고문을 무시해준다.
- Hashable Protocol - Apple, 스위프트의 기본 데이터 타입은 모두 Hashable Protocol을 준수(conform)한다.