본문 바로가기

학습 노트/Swift (2021)

087 ~ 097. Collections (콜렉션)

Collection (컬렉션)

Swift는 세 가지의 컬렉션을 제공한다.

  • Array (배열)
    데이터를 순서대로 저장한다.
  • Dictionary (딕셔너리)
    key와 value를 하나의 쌍으로 저장한다.
  • Set (셋/집합)
    집합 연산을 수행하는 컬렉션.

Foundation Collection과 Swift Collection

Swift의 컬렉션 은은 다시 두 가지로 구분되는데,
기존부터 사용되던 Class로 구현된 Foundation Collection과 구조체로 구현된 Swift Collection이다.
일반적으로 말하는 컬렉션은 Swift Collection을 의미한다.
따라서 Swift Collection의 사용 빈도가 많고, 값 형식이 아닌 참조 형식을 사용하고 싶을 때 Foundation Collection을 사용한다.

Foundation
Collection
Swift
Collection
NSArray
NSDictionary
NSSet
Array
Dictionary
Set
객체 형식으로만 저장 객체 형식과
값 형식을 모두 지원
자료형에 제한이 없음 동일한 자료형만 저장 가능
NSArray
NSDictionary
NSSet

NSMutableArray
NSMutableDictionary
NSMutableSet
키워드로 선언함

let
var

NS접두어가 이름에 포함된 경우 Foundation Collection으로 구분하면 된다.

컬렉션에 저장되는 값들은 Element(요소)라고 부른다.
컬렉션의 가변/불변성은 컬렉션 내의 요소에는 영향을 주지 않는다.
Swift 컬렉션은 값 형식이기 때문에 사용 시 복사를 하게 된다.
무분별하게 사용되는 경우 메모리 관리의 문제가 생겨 String과 마찬가지로 Cpoy-on-write 방식을 따른다.

 

Array (배열)

0 1 2 3 4
         

배열은 요소를 순서대로 저장하는 Ordered Collection이다

0 1 2 3 4
Int Int Int Int Int

배열에 저장되는 자료형은 반드시 모두 동일해야 한다.

0 1 2 3 4
안녕 String String String String

이러한 배열의 자료형은 첫 번째 요소가 저장되는 시점에 요소의 자료형에 의해 결정된다.

0 1 2 3 4
13 8 17 8 8

각각의 요소들은 다른 값을 가질 수 있으며, 값이 같더라도 같은 요소로 취급되지 않는다.

0 1 2 3
13 8 8 8

중간의 요소가 지워지면 이곳은 빈 요소가 되는 것이 아닌 뒤에 따르는 요소들이 당겨지게 된다.
또한, 삭제된 만큼 배열의 길이도 짧아진다.

0 1 2 3 4
13 8 8 8 70

새로운 값이 저장되면 배열 마지막에 저장된다.

 

Syntax

[elem, elem, ...]

 

let number = [1, 2, 3]

type(of: number)
결과

[Int]

배열 생성 시 정수를 저장했으므로 정수 배열이 생성된다.

배열 생성하기

let number = [1, 2, 3]
결과

[1, 2, 3]

배열에 값을 저장하면서 생성하는 방법이 가장 간단하지만 빈 배열을 만들어야 하는 경우엔 생성자를 활용한다.

let empty1 = []
결과

//error

배열의 자료형은 저장되는 요소에 의해 결정이 되는데, 저장되는 요소가 없으면 형식 추론이 불가하기 때문에 오류가 발생한다.
이 때는 생성자를 사용하여 빈 배열을 생성할 수 있다.

let empty2 = Array<Int>()
let empty3 = [Int]()
결과

[]
[]

생성자를 사용하면 반복되는 값을 쉽게 저장하는 것도 가능하다.

let empty4 = [Int](repeating: 0, count: 100)
결과

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Inspecting an Array (배열 검토하기)

let empty4 = [Int](repeating: 0, count: 100)

empty4.count
결과

100

배열이 비어있는지를 확인하는 방법은 두 가지가 있다.

let empty4 = [Int](repeating: 0, count: 100)

empty4.count == 0
empty4.isEmpty
결과

false
false

선택적으로 사용할 수 있지만 주로 사용이 권장되는 것은 isEmpty 속성이다.

 

Accessing Elements (요소 접근하기)

let fruits = ["apple", "banana", "carrot"]

fruits[2]
결과

"carrot"

요소에 접근할 때는 서브 스크립트 방식으로 접근한다.
이때 index의 시작이 0 임에 주의하자.

let fruits = ["apple", "banana", "carrot"]

fruits[0...1]
결과

["apple", "banana"]

index의 범위를 전달해서 여러 개의 요소에 접근할 수도 있다.
결과는 index 0~1에 해당하는 "apple"과 "banana"로 이루어진 새로운 배열을 반환한 모습이다.

이렇게 서브 스크립트 방식으로 배열에 접근할 때에는 잘못된 범위나 잘못된 index를 전달해 에러가 발생하지 않도록 조심해야 한다.
따라서 다음의 방법을 사용하기도 한다.

let fruits = ["apple", "banana", "carrot"]

fruits[fruits.startIndex]
fruits[fruits.index(before: fruits.endIndex)]

fruits.first
fruits.last
결과

"apple"
"carrot"
"apple"
"carrot"

index를 직접 전달하는 것이 아닌, 속성으로 전달하는 방법이다.
startIndex는 시작하는 Index를, index(before: fruits.endIndex)는 마지막 index를 나타낸다.
이때 endIndex를 바로 전달하면 오류가 발생하는데, string과 마찬가지로 배열의 마지막 index도 마지막 요소를 가리키지 않는다.
따라서 endIndex의 직전 index가 마지막 요소의 index라는 것을 기억해 두자.

let empty = [String]()

empty.first
empty.last
결과

nil
nil

이후의 first와 last속성은 그 자체로 첫 번째와 마지막 요소를 가리킨다.
또한 비어있는 배열이라도 오류가 발생하지 않고 nil을 반환해 오류를 방지할 수 있다는 장점이 있다.
다만 이들은 중간의 다른 index를 가리킬 수 없기 때문에 사용이 제한적이다.

 

Adding Elements (요소 추가하기)

let fruits = ["apple", "banana", "carrot"]

fruit append("durian")
fruits
결과

//error

배열에 새 요소를 추가하거나 제거하는 등의 편집을 위해서는 let이 아닌 var로 가변 배열로 선언해야 한다.

var fruits = ["apple", "banana", "carrot"]

fruits.append("durian")
fruits
결과

["apple", "banana", "carrot", "durian"]

배열의 마지막에 요소를 추가하는 경우 append() method를 사용하여 원래의 배열의 자료형에 맞는 값을 저장할 수 있다.

var fruits = ["apple", "banana", "carrot"]

fruits.append(contentsOf: ["durian", "eggplant"])
fruits
결과

["apple", "banana", "carrot", "durian", "eggplant"]

두 개 이상의 요소를 한 번에 추가하는 경우 append(contentsOf:) method를 사용해 전달하고자 하는 값들을 배열로 전달한다.

var fruits = ["apple", "banana", "carrot", "durian", "eggplant"]

fruits.insert("cherry", at: 2)
fruits
결과

["apple", "banana", "cherry", "carrot", "durian", "eggplant"]

배열의 중간에 하나의 요소를 추가하는 경우 insert method를 사용한다.
이때 at: 에 전달될 index는 추가될 index가 전달되어야 한다.

var fruits = ["apple", "banana", "durian", "eggplant"]

fruits.insert(contentsOf: ["cherry", "carrot"], at: 2)
결과

["apple", "banana", "cherry", "carrot", "durian", "eggplant"]

contentsOf parameter에 배열로 삽입할 값을 전달하고,
at: 에 삽입할 위치를 전달한다.

요소의 범위를 바꾸는 방법은 두 가지가 있다.

var fruits = ["apple", "banana", "cherry", "carrot", "durian", "eggplant"]

fruits[3...5] = ["durian", "eggplant", "florida gooseberry"]
fruits
결과

["apple", "banana", "cherry", "durian", "eggplant", "florida gooseberry"]

첫 번째는 위와 같이 서브 스크립트로 접근하여 바꾸는 방법
두 번째는 아래와 같이 method로 값을 바꾸는 방법이다.

var fruits = ["apple", "banana", "cherry", "durian", "eggplant", "florida gooseberry"]

fruits.rplaceSubrange(3...5, with: ["carrot", "durian", "eggplant"])
fruits
결과

["apple", "banana", "cherry", "carrot", "durian", "eggplant"]

배열 범위를 바꿀 때는 반드시 요소의 수와 대체하려는 값의 수가 일치할 필요는 없다.

var fruits = ["apple", "banana", "cherry", "durian", "eggplant", "florida gooseberry"]

fruits.rplaceSubrange(3...5, with: ["carrot", "eggplant"])
fruits

 

결과

["apple", "banana", "cherry", "durian", "eggplant"]

값의 수가 부족하면 부족한 대로 오류 없이 대체된다.
따라서 전달하는 값을 빈 배열로 대체하게 되면 해당 범위를 삭제하는 것도 가능하다.

 

Removing Elements (요소 제거하기)

var fruits = ["apple", "banana", "cherry", "durian", "eggplant"]

fruits.remove(at: 4)
fruits
결과

"eggplant"
["apple", "banana", "cherry", "durian"]

요소 하나를 삭제할 때는 .remove(at:) method를 사용한다.
parameter인 at에는 삭제할 요소의 index를 전달하고, 해당 요소를 삭제 한 뒤, 삭제된 요소를 반환한다.
이때에도 잘못된 index를 전달하지 않도록 주의하도록 하자.

var fruits = ["apple", "banana", "cherry", "durian"]

fruits.removeFirst()
fruits

fruits.removeFirst(2)
fruits
결과

apple
["banana", "cherry", "durian"]
["durian"]

첫 번째 요소를 삭제하는 방법에는 세 가지가 있다.
remove method를 사용해 직접 index 0을 전달하는 방법과
removeFirst method를 사용하는 방법이다.
removeFirst method를 parameter 없이 사용하면 맨 처음의 요소를 삭제하고 삭제된 요소를 반환한다.
하지만 parameter로 정수를 전달하면 맨 처음부터 정수만큼의 요소를 반환 없이 삭제한다.

마지막 method를 삭제하는 것도 removeLast method를 사용하면 된다.
사용법은 동일하다.

var fruits = ["apple", "banana", "cherry", "durian"]

fruits.removeAll()
결과

[]

배열 안의 요소를 전부 삭제하고 이를 반환한다.
이렇게 배열이 removeAll로 비어있는 상태에서 removeFirst나 removeLast method를 사용하게 되면 오류가 발생하니 주의하자.

var fruits = ["apple", "banana", "cherry", "durian"]

fruits.removeAll()
fruits.popLast()
결과

nil

이는 removeFirst와 removeLast의 반환 값이 optional이 아니기 때문인데,
반환 값이 optional인 popFirst와 popLast를 사용하면 nil을 반환할 수 있으므로 조금 더 안전한 method이다.

범위를 삭제하는 방법은 두 가지이다.
removeSubrange() method를 사용해 삭제하는 방법,
서브 스크립트를 사용해 삭제하는 방법이다.

var fruits = ["apple", "banana", "cherry", "durian"]

fruits.removeSubrange(1...2)

fruits[0...1] = []
fruits
결과

["apple", "durian"]
[]

Comparing Arrays

let a = ["A", "B", "C"]
let b = ["a", "b", "c"]

a == b
a != b
결과

flase
true

비교 연산자는 요소들 각각을 비교한 다음, 요소들이 저장된 순서도 비교한다.

let a = ["A", "B", "C"]
let b = ["a", "b", "c"]

a.elementsEqual(b)

 

결과

false
let a = ["A", "B", "C"]
let b = ["a", "b", "c"]

a.elementsEqual(b) { (lhs, rhs) -> Bool in
	return lhs.caseInsensitiveCompare(rhs) == .orderedSame
}
결과

true

비교 연산 method인 elementsEqual은 parameter로 다른 배열을 전 닳거나,
비교 조건을 직접 지정하고 싶은 경우 클로저를 함께 전달할 수 있다.
두 번째 코드는 클로저를 전달해 caseInsensitiveCompare method를 사용해 대소 구별을 무시한 코드이다.

Finding Elements

let num = [1, 2, 3, 4, 5, 6, 7]

num.contains(5)
num.contains(9)
결과

true
false

contains method는 parameter로 전달된 값이 배열에 존재하는지를 판단한다.
이 method도 마찬가지로 closure를 전달해 검색 조건을 직접 지정할 수 있다.

contains()
input value
closure
output Bool
let num = [1, 2, 3, 4, 5, 6, 7]

num.contains { (n) -> Bool in
	return n % 2 == 0
}
결과

true

contains는 존재 유무를 판단하기 때문에 true가 반환된다.

let num = [1, 2, 3, 4, 5, 6, 7]

num.first { (n) -> Bool in
	return n % 2 == 0
}

num.firstIndex { (n) -> Bool in
	return n % 2 == 0
}
결과

2
1

first method는 요소를 반환하고, firstIndex method는 index를 반환한다.
firstIndex method에 parameter로 값을 전달하면 해당하는 index를 반환한다.

  firstIndex() first()
input closure value
closure
output elem? Int?
let num = [1, 2, 3, 4, 5, 6, 7]

num.firstIndex(of: 4)
결과

3

.firstIndex method에 parameter로 값을 전달하면 해당하는 index를 반환한다.
lastIndex, last method도 검색 순서가 반대인 점을 제외하면 같다.

  lastIndex() last()
input closure value
closure
output elem? Int?

Sorting Array

정렬을 배열하는 방법은 배열을 직접 재 정렬하는 방법과, 정렬된 새 배열을 반환하는 방법 두 가지로 분류된다.
이는 이전에 정리한 method 구분법과 비슷하게 sort와 sorted로 구분한다.

let num = [1, 5, 8, 8, 3, 0, 8, 2]

num.sort()
결과

//error

따라서 sort() method의 경우 배열을 직접 정렬하기 때문에 let으로 생성한 불변 배열에는 사용할 수 없다.

let num = [1, 5, 8, 8, 3, 0, 8, 2]

num.sorted()
num

 

결과

[0, 1, 2, 3, 5, 8, 8, 8]
[1, 5, 8, 8, 3, 0, 8, 2]

sorted는 오름차순으로 재 정렬한 배열을 반환했지만
원본은 처음 입력한 그대로 남아있는 것을 확인할 수 있다.

내림차순으로 정렬을 구현하는 방법에는 두 가지가 있다.

let num = [1, 5, 8, 8, 3, 0, 8, 2]

num.sorted { (a, b) -> Bool in
	return a > b
}
결과

[8, 8, 8, 5, 3, 2, 1, 0]

클로저로 내림차순 정렬을 직접 구현했다.

let num = [1, 5, 8, 8, 3, 0, 8, 2]

num.sorted().reversed()

 

결과

ReversedCollection<Array<Int>>

오름차순 정렬을 reversed method를 사용하여 반전시켰다.
하지만 결과가 의도한 결과가 아닌데 이는 reversed method가 배열을 반환하지 않기 때문이다.
원래의 배열의 메모리를 공유하면서 이를 반전시킨 순서로 인식하도록 만드는 구조이다.
만약 새 배열을 반환받고자 한다면 배열 생성자를 사용하면 된다.

let num = [1, 5, 8, 8, 3, 0, 8, 2]

[Int](num.sorted().reversed())
결과

[8, 8, 8, 5, 3, 2, 1, 0]

이렇게 우리가 의도한 결과가 반환되게 된다.
배열을 직접 재 정렬하고 싶다면

  1. 가변 배열일 것.
  2. sort method를 사용할 것.

두 가지의 조건을 충족시키면 된다.

var num2 = [1, 5, 8, 8, 3, 0, 8, 2]

num2.sort()
num2.sort().reverse()
결과

[0, 1, 2, 3, 5, 8, 8, 8]
[8, 8, 8, 5, 3, 2, 1, 0]

reverse method는 반환 없이 원본 배열을 직접 수정하기 때문에 생성자를 사용하지 않아도 된다.

Swap Elements

var num2 = [1, 5, 8, 8, 3, 0, 8, 2]

num2.swapAt(3, 4)
결과

[1, 5, 8, 3, 8, 0, 8, 2]

요소의 위치를 바꾸고 싶을 때는 SwapAt method를 사용할 수 있다.
서로 바꾸고자 하는 index를 전달하면 된다.

Shuffle Array

var num2 = [1, 5, 8, 8, 3, 0, 8, 2]

num2.shuffle()
결과

[1, 2, 8, 5, 3, 8, 0, 8]

배열의 정렬을 무작위로 바꾼다.

 

Dictionary (딕셔너리)

딕셔너리는 요소가 key와 value로 쌍을 이루는 컬렉션이다.
이때 key는 한 딕셔너리 내에서 유일해야 한다.
딕셔너리는 index가 존재하지만 사용할 수 없고, 이를 정렬할 수도 없다. (Unordered Collection)
또한 딕셔너리에 저장되는 모든 요소의 key와 value의 자료형은 각각 전부 일치해야 한다. (Single Type)

 

Syntax

[key1: value1, key2: value2, ...]

 

var dict = ["A": "Apple", "B": "Banana", "C", "Cherry"]
dict = [:]

딕셔너리는 key와 value가 한 쌍으로 요소를 이룬다.
따라서 위에서 생성한 딕셔너리는 3개의 요소를 가진다.
또한 배열과 마찬가지로 스퀘어 브래킷을 사용하기 때문에 콜론의 역할이 매우 중요하다고 할 수 있다.

 

Dictionary Type

Dictionary<K, V>
[K:V]

 

let dict1: Dictionary<String, Int>
let dict2: [String:Int]

딕셔너리의 표현식은 배열과 마찬가지로 정식 문법과 단축 문법 두 가지가 있다.
또한 마찬가지로 단축 문법을 줄 사용 한다.

Creating Dictionary (딕셔너리 생성하기)

let words = ["A": "Apple", "B": "Banana", "C", "Cherry"]

let과 var 키워드로 불변/가변성을 정의하고 단축 문법으로 요소들을 전달한다.

let empty1: [String: String] = [:]
let empty2 = [String: String]()
let empty3 = Dictionary<String, String>

딕셔너리도 배열과 마찬가지로 요소가 저장될 때 요소의 자료형을 결정하는데 빈 딕셔너리의 경우 불가능하다.
따라서 자료형을 직접 지정해 주거나, 생성자를 사용해서 빈 딕셔너리를 만들어야 한다.

Inspecting Dictionary (딕셔너리 검토하기)

let words = ["A": "Apple", "B": "Banana", "C", "Cherry"]

words.count
words.isEmpty
결과

3
false

배열의 요소 수를 알고 싶다면 count 속성을 사용한다.
배열이 비어있는지 확인하고 싶다면 isempty 속성을 사용한다.

Accessing Keys and Values (키와 값에 접근하기)

let words = ["A": "Apple", "B": "Banana", "C", "Cherry"]

words["A"]
결과

Apple

값에 접근할 때는 서브 스크립트 문법을 사용한다.
이때 키를 전달하면 해당 값에 접근할 수 있다.

let words = ["A": "Apple", "B": "Banana", "C", "Cherry"]

words["Apple"]

 

결과

nil

키로는 값에 접근할 수 있지만 값으로는 키에 접근 할 수 없다.
딕셔너리는 값으로 키에 접근 할 수 있는 방법을 제공하지 않는다.
따라서 전달한 "Apple"을 키로 판단해 해당 값을 찾으나 존재하지 않아 nil을 반환한다.

let words = ["A": "Apple", "B": "Banana", "C", "Cherry"]

let a = words["T"]
type(of: a)
결과

Optional<String>.Type

존재하지 않는 경우 nil을 반환하기 때문에 Optional 형식을 가진다.

let words = ["A": "Apple", "B": "Banana", "C", "Cherry"]

let a = words["T", default: "value is not exist"]

 

결과

value is not exist

키와 함께 default를 전달하면 값이 없을 경우 반환할 기본값을 정할 수 있다.

let words = ["A": "Apple", "B": "Banana", "C", "Cherry"]

let a = words["T", default: "value is not exist"]
type(of: a)
결과

value is not exist
String

또한 이 경우 반환되는 값의 자료형을 갖게 된다.
키를 전달했을 때 반환될 값이 있다면 값과 같은 자료형을, 없다면 nil을 전달하고 Optional의 형식을 취한다.

딕셔너리는 키와 값을 개별적으로 제공하는 속성을 가지고 있다. ```swift let words = ["A": "Apple", "B": "Banana", "C", "Cherry"]

let words = ["A": "Apple", "B": "Banana", "C", "Cherry"]

for k in words.keys {
	print(k)
}
결과

B
A
C

딕셔너리가 가진 키들은 keys 속성으로 확인할 수 있다.
다만 반환 값이 배열의 형태가 아니기 때문에 반복문의 형태로 반환받아야 한다.
딕셔너리는 순서가 없는 collection이기 때문에 출력 결과는 무작위다.

let words = ["A": "Apple", "B": "Banana", "C", "Cherry"]

for k in words.values {
	print(k)
}
결과

Cherry
Apple
Banana

딕셔너리의 값들은 values 속성으로 확인할 수 있다.
마찬가지로 배열의 형태는 아니기 때문에 반복문의 형태로 받아와야 한다.
이 또한 딕셔너리는 순서가 없는 collection이기 때문에 출력 결과는 무작위다.

keys와 values 모두 배열은 아니지만 배열과 비슷한 기능들을 제공한다.

let words = ["A": "Apple", "B": "Banana", "C", "Cherry"]

for k in words.values.sorted() {
	print(k)
}
결과

Apple
Banana
Cherry

딕셔너리 자체는 정렬이 불가능하지만, 속성으로 반환받은 값들은 정렬할 수 있다.

let words = ["A": "Apple", "B": "Banana", "C", "Cherry"]

let keys = Array(words.key)
let values = Array(words.value)
결과

["C", "A", "B"]
["City", "Apple", "Banana"]

내용물을 배열의 형태로 반환받고 싶다면 배열 생성자에 전달해 주면 된다.

 

Adding Keys and Values

새로운 요소를 추가할 때는 key를 기준으로 추가하며, 서브 스크립트가 가장 단순하다.

var words = [String: String]()

words["A"] = "Apple"
words["B"] = "Banana"
words.count
words
결과

2
["A": "Apple", "B": "Banana"]

key를 기준으로 요소를 추가하기 때문에 서브 스크립트에는 저장할 key를 전달한다.

var words = [String: String]()

words["A"] = "Apple"
words["B"] = "Banana"

//----

words["B"] = "Ball"
words.count
words
결과

2
["A": "Apple", "B": "Ball"]

key가 존재하지 않는 경우 새로운 요소를 추가하고,
key가 존재하는 경우 기존의 값을 교체한다.

var words = [String: String]()

words["A"] = "Apple"
words["B"] = "Banana"

//---

words.updateValue("City", forKey: "C")
words.count
words
결과

nil
3
["A": "Apple", "B": "Ball", "C": "City"]

method를 사용하는 경우 updateValue method를 사용한다.
이 경우에도 마찬가지로 key가 존재하지 않는 경우 새로운 요소를 추가하나 마나 nil을 반환하고
key가 존재하는 경우 기존의 값을 교체하면서 기존의 값을 반환한다.

이와 같이 없으면 추가하고, 있으면 교체하는 동작은 insert + update = upsert라고 부르기도 한다.

Removing Keys and Values

삭제도 key 기준으로 진행하며 서브 스크립트 문이 가장 단순하다.

var words = ["A": "Apple", "B": "Ball", "C": "City"]

words["B"] = nil
words.count
words
결과

nil
2
["C": "City", "A": "Apple"]

원하는 요소의 key와 nil을 전달하면 해당 요소가 삭제된다.

var words = ["A": "Apple", "B": "Ball", "C": "City"]

words["Z"] = nil
words.count
words
결과

nil
3
["A": "Apple", "B": "Ball", "C": "City"]

전달한 key가 존재하지 않을 경우 아무 변화가 생기지 않는다.

var words = ["A": "Apple", "B": "Ball", "C": "City"]

words.removeValue(forKey: "A")
words.count
words
결과

Apple
2
["B": "Ball", "C": "City"]

method를 사용할 때에는 removeValue를 사용한다.
삭제할 요소의 key를 전달하면 삭제한 요소의 value를 반환한다.
삭제할 요소가 존재하지 않는 경우 nil을 반환한다.

var words = ["A": "Apple", "B": "Ball", "C": "City"]

words.removeAll()
결과

[:]

전체 요소를 삭제할 때는 removeAll을 사용한다.

 

Comparing Dictionaries

딕셔너리도 배열이나 문자열과 같이 비교 시 대소문자를 구별한다.
또한 딕셔너리의 경우 요소들의 저장 순서가 없으나, 비교 시 이에 구애받지 않는다.

비교 연산자를 사용해 비교할 수 있다.

let a = ["A": "Apple", "B": "Banana", "C": "City"]
let b = ["A": "Apple", "C": "City", "B": "Banana"]
let c = ["A": "Apple", "C": "City", "B": "banana"]

a == b
a != b
b != c
결과

true
false
true

따라서 a와 b는 같고, b와 c는 다르다.

method를 사용할 때에는 elementsEqual을 사용한다.

대소문자의 구별을 무시하고 싶다면 method를 사용해야 하는데,
이전의 비교들과 다른 점이 있다.

let a = ["A": "Apple", "B": "Banana", "C": "City"]
let b = ["A": "Apple", "C": "City", "B": "banana"]

a.elementsEqual(b) { (lhs, rhs) -> Bool in
	print(lhs.key, rhs.key)
	return lhs.key.caseInsensitiveCompare(rhs.key) == .orderedSame && lhs.value.caseInsensitiveCompare(rhs.value) == .orderedSame
}
결과

false
B A
false

딕셔너리의 요소를 하나씩 불러와 해당 key를 출력하고 value에 caseInsensitiveCompare 속성을 부여하고 비교하는 코드이다.
여러 번 실행해 보면 출력도 일정하지 못하고, 결과도 중구난방인 것을 확인할 수 있는데 이는 dictionary의 '순서가 없다.'라는 특징 때문이다.
따라서 이런 비교를 진행하기 전, 올바른 비교대상의 요소를 불러올 수 있도록 해 주어야 한다.

let a = ["A": "Apple", "B": "Banana", "C": "City"]
let b = ["A": "Apple", "C": "City", "B": "banana"]

let aKeys = a.keys.sorted()
let bKeys = b.keys.sorted()

a.elementsEqual(aKeys) { (lhs, rhs) -> Bool in
	guard lhs.caseInsensitiveCompare(rhs) == .orderedSame else {
		return false
	}
	guard let lv = a[lhs], let rv = b[rhs], lv.caseInsensitiveCompare(rv) == .orderedSame else {
		return false
	}
}
  1. 각 딕셔너리의 key 값들을 정렬한다.
  2. 정렬된 key 값을 비교한다. 같다면 다음 guard문을 진행하고, 다르다면 즉시 false를 출력하고 종료한다.
  3. 각 key 들에 대응하는 value 값들을 비교한다. 이때 caseInsensitiveCompare 속성을 사용해 대소 구분을 무시한다. 같다면 true를 출력 후 종료하고, 다르다면 득시 false를 리턴하고 종료한다.

Finding Elements

딕셔너리의 검색은 클로저를 사용해 구현해야만 한다.

var words = ["A": "Apple", "B": "Banana", "C": "City"]

let c: ((String, String)) -> Bool = {
	$0.0 == "B" || $0.1.contains("i")
}

words.contains(where: c)
words.first(where: c)
결과

true
(key "B", value "Banana")
  1. key와 value의 자료형을 클로저로 전달한다.
  2. key의 값이 B이거나 value의 값에 i가 포함되어 있으면 true를 반환한다.
  3. contains method는 클로저를 전달하며, 딕셔너리를 비교할 때 일치하는 조건이 하나라도 있으면 true를 반환한다.
  4. first(where:) method는 마찬가지로 클로저를 전달하며, 조건에 일치하는 첫 번째 요소를 Optional tuple로 반환한다.
  5. 이때 딕셔너리의 순서가 무작위 이므로, 실행할 때마다 first의 결과는 달라질 수 있다.
var words = ["A": "Apple", "B": "Banana", "C": "City"]

let c: ((String, String)) -> Bool = {
    $0.0 == "B" || $0.1.contains("i")
}

let r = words.first(where: c)
r?.key
r?.value
결과

(key "B", value "Banana")
"B"
"Banana"

반환된 값을 변수나 상수로 받아서 key와 value 에도 접근할 수 있다.

비슷하게 사용할 수 있는 method로 filter가 있는데

var words = ["A": "Apple", "B": "Banana", "C": "City"]

let c: ((String, String)) -> Bool = {
	$0.0 == "B" || $0.1.contains("i")
}

words.filter(c)
결과

["C": "City", "B": "Banana"]

조건과 일치하는 요소만 반환하는 method이다.

 

Set (셋)

배열과 딕셔너리에 비해 사용빈도가 낮지만, 검색 속도를 우선시하려는 경우 사용되기도 한다.
셋은 배열과 같이 한 가지의 데이터를 한 요소로 취급한다.
단, 모든 요소의 자료형은 동일해야 한다.
배열과 다르게 정렬되지 않으며, index도 사용하지 않고, 동일한 값을 중복하여 저장할 수 없다.

셋이 배열에 비해 빠른 검색 속도를 가지는 이는 해싱 알고리즘을 사용하기 때문으로,
이는 값들을 고정된 길이의 값들로 변환하는 것으로, indexing과 암호화에서 사용 빈도가 높다.

Syntax

Set = [elem1, elem2, elem3, ...]
let set1 = [1, 2, 3, 3, 3, 3]
set1.count

let set2: Set<Int> = [1, 2, 3, 3, 3, 3]
set2.count

//요소의 자료형은 생략 가능
let set3: Set = [1, 2, 3, 3, 3, 3]
set3.count

 

결과

[1, 2, 3, 3, 3, 3]
6
{1, 2, 3}
3

첫 번째는 배열이고 두 번째는 셋이다.
같은 데이터를 저장하지만 배열은 6개 모두 저장된 반면, 셋은 중복되는 3 세 개를 제외한 3개만이 저장된 것을 볼 수 있다.
셋도 형식 추론을 지원하기 때문에 다음과 같이 단축해서 사용할 수 있다.

let set3: Set = [1, 2, 3, 3, 3, 3]
set3.count
결과

{1, 2, 3}
3

Inspecting a Set (셋 검토하기)

let set3: Set = [1, 2, 3, 3, 3, 3]

set3.count
set3.isEmpty
결과

3
false

다른 컬렉션들과 동일하게 count와 isEmpty 속성을 사용할 수 있다.

Testing for Membership (요소 확인하기)

셋을 사용할 때 가장 자주 사용하는 기능으로, 요소가 셋 안에 포함되었는지 확인하는 작업이다.

set3.contains(1)
set3.contains(5)
결과

true
false

 

전달한 값이 존재한다면 true를, 존재하지 않는다면 false를 반환한다.

Adding and Removing Elements

var words = Set<String>()

var insertResult = words.insert("Swift")
insertResult.inserted
insertResult.memberAfterInsert
결과

(inserted true, memberAfterInsert "Swift")
true
"Swift"

셋에 요소를 추가하기 위해선 insert method를 사용한다.
셋에서의 insert는 inserted와 memeberAfter를 반환하는데, 각각 성공 여부와 삽입 값을 반환한다.

var words = Set<String>()

var insertResult = words.insert("Swift")
insertResult.inserted
insertResult.memberAfterInsert

insertResult = words.insert("Swift")
insertResult.inserted
insertResult.memberAfterInsert

words
결과

(inserted true, memberAfterInsert "Swift")
true
"Swift"
(inserted false, memberAfterInsert "Swift")
false
"Swift"
{"Swift"}

중복된 값을 저장하려고 하면 false와 시도했던 값을 반환한다.
또한 오류가 발생하지 않고, 셋에는 추가되지 않는다.

셋은 해쉬값을 통해 값을 구분한다.

var words: Set = ["Swift"]

var value = "Swift"
value.hashvalue

updatedResult = words.update(with: value)
updatedResult
결과

"Swift"
3543674583216325307
"Swift"
"Swift"

update() method는 with parameter로 전달된 값으로 값을 변경한다.
words 셋에는 이미 "Swift"가 존재하지만 value의 Swift와 해쉬값이 동일해 값을 바꿀 수 있다.
값이 같아 확인하기 어렵기 때문에 다른 방식으로 접근해 본다.

struct Sample: Hashable {
	var hashValue: Int = 123
	var data: String
	
	init(_ data: String) {
		self.data = data
	}
	
	static func ==(lhs: Sample, rhs: Sample) -> Bool {
		return lhs.hashValue == rhs.hashValue
	}
}

해당 구조체는 해쉬값을 123으로 고정하는 구조체이다.

struct Sample: Hashable {
	var hashValue: Int = 123
	var data: String
	
	init(_ data: String) {
		self.data = data
	}
	
	static func ==(lhs: Sample, rhs: Sample) -> Bool {
		return lhs.hashValue == rhs.hashValue
	}
}

var sampleSet = Set<Sample>()

var data = Sample("Swift")
data.hashValue
결과

123

내용이 어떤 것이건 간에 123으로 고정된다.

struct Sample: Hashable {
	var hashValue: Int = 123
	var data: String
	
	init(_ data: String) {
		self.data = data
	}
	
	static func ==(lhs: Sample, rhs: Sample) -> Bool {
		return lhs.hashValue == rhs.hashValue
	}
}

var sampleSet = Set<Sample>()

var data = Sample("Swift")
data.hashValue

//-----

var r = sampleSet.insert(data)
r.inserted
r.memberAfterInsert
sampleSet
결과

true
Sample
{{hashValue 123, data "Swift"}}

123에 해당하는 해쉬가 없는 빈 셋이므로 inser() method는 정상적으로 새로운 요소를 추가할 수 있다.

data.data = "Hello"
data.hashValue
결과

Sample
123

해쉬값은 123으로 고정된 상태에서 내용만 Swift -> Hello로 변경했다.

struct Sample: Hashable {
	var hashValue: Int = 123
	var data: String
	
	init(_ data: String) {
		self.data = data
	}
	
	static func ==(lhs: Sample, rhs: Sample) -> Bool {
		return lhs.hashValue == rhs.hashValue
	}
}

var sampleSet = Set<Sample>()

var data = Sample("Swift")
data.hashValue

var r = sampleSet.insert(data)
r.inserted
r.memberAfterInsert
sampleSet

data.data = "Hello"
data.hashValue

//-----

r = sampleSet.insert(data)
r.inserted
r.memberAfterInsert
sampleSet

sampleSet.update(with: data)
sampleSet
결과

(inserted false, {hashValue 123, data "Swift"})
false
Sample
{{hashValue 123, data "Swift"}}
Sample
{{hashValue 123, data "Hello"}}

해쉬값 123을 가진 요소가 이미 셋 안에 존재하기 때문에 inser() method는 실패한다.
하지만 123을 가진 요소가 이미 존재하기 때문에 update() method는 동작할 수 있고,
결과 역시 Hello로 바뀐 것을 확인할 수 있다.

Removing Elements

var words: Set = ["Swift", "Apple"]

words
words.remove("Swift")
words

words.remove("ghost")

 

결과

{"Swift", "Apple"}
"Swift"
{"Apple"}
nil

배열과 비슷하지만 index가 존재하지 않기 때문에 사용 방법이 조금 다르다.
parameter로 index를 전달하는 것이 아닌 요소의 내용을 정확히 전달해야 한다.
만약 해당 요소가 존재하지 않는 경우 nil을 반환한다.

셋을 완전히 비우려는 경우 removeAll() method를 사용한다.

words.removeAll()
words
결과

Set([])
Set([])

Comparing Sets (집합 비교하기)

var a: Set = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var b: Set = [1, 3, 5, 7, 9]
var c: Set = [2, 4, 6, 8, 10]
let d: Set = [1, 7, 5, 9, 3]

a == b
a != b

b == d
b != d
결과

false
true
true
false

셋에도 같거나 다름을 확인하는 데에 동일하게 비교 연산자를 사용할 수 있다.
또한 순서가 없기 때문에 저장하는 순서가 달라도 상관없다.

var a: Set = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var b: Set = [1, 3, 5, 7, 9]
var c: Set = [2, 4, 6, 8, 10]
let d: Set = [1, 7, 5, 9, 3]

//-----

b.elementsEqual(d)
결과

false

순서가 없기 때문에 생기는 문제점도 동일하게 가지고 있다.
elementsEqual method는 값을 순서대로 비교하기 때문에 셋을 비롯한 순서가 없는 컬렉션에는 적용하기 부적합하다.
반드시 사용해야 한다면 배열로 변환 후, 같은 조건으로 정렬하여 사용할 수 있다.

셋은 집합의 개념으로 셋의 비교에는 집합 관련 비교가 포함된다.
부분집합, 진부분 집합, 상위집합, 진상위집합, 서로소로 하나씩 정의를 생각해 보면서 확인해 본다.

var a: Set = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var b: Set = [1, 3, 5, 7, 9]
var c: Set = [2, 4, 6, 8, 10]
let d: Set = [1, 7, 5, 9, 3]

a.isSubset(of: a)
a.isStrictSubset(of: a)
결과

true
false
부분집합
A ⊂ B

진부분집합
A ⊂ B 이고 A ≠ B

자기 자신은 스스로의 부분집합이기 때문에 부분집합의 여부를 판단하는 isSubset method의 결과는 true이다
자기 자신은 스스로의 부분집합이지만 진부분 집합은 서로 같은 집합이면 성립하지 않으므로,
진부분 집합을 판단하는 isStrictSubset 메소드의 결과는 false이다.

var a: Set = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var b: Set = [1, 3, 5, 7, 9]
var c: Set = [2, 4, 6, 8, 10]
let d: Set = [1, 7, 5, 9, 3]

b.isSubset(of: a)
b.isStrictSubset(of: a)
결과

true
true

b가 a의 모든 원소를 가지고 있으므로 b는 a의 부분집합이다. 따라서 isSubset의 결과는 true이다.
b가 a의 부분집합이고, 서로 같은 집합이지도 않기 때문에 isStrictSubset의 결과는 true이다.

var a: Set = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var b: Set = [1, 3, 5, 7, 9]
var c: Set = [2, 4, 6, 8, 10]
let d: Set = [1, 7, 5, 9, 3]

a.isSuperset(of: a)
a.isStrictSuperset(of: a)
결과

true
false
상위집합
A ⊃ B

진상위집합
A ⊃ B 이고 A ≠ B

자기 자신은 스스로의 상위 집합이므로 상위 집합을 여부를 판단하는 isSuperset method의 결과는 true이다.
상위 집합이면서 두 집합이 같지 않다면 진상 위집합이지만 같으므로,
진상위집합 여부를 판단하는 isStrictSuperset method의 결과는 false이다.

var a: Set = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var b: Set = [1, 3, 5, 7, 9]
var c: Set = [2, 4, 6, 8, 10]
let d: Set = [1, 7, 5, 9, 3]

a.isSuperset(of: b)
a.isStrictSuperset(of: b)
결과

true
true

b의 모든 원소가 a에 존재하므로 isSuperset의 결과는 true이다.
a가 b의 상위 집합이고, 두 집합이 서로 같지 않으므로 isStrictSuperset의 결과는 참이다.

var a: Set = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var b: Set = [1, 3, 5, 7, 9]
var c: Set = [2, 4, 6, 8, 10]
let d: Set = [1, 7, 5, 9, 3]

a.isSuperset(of: c)
a.isStrictSuperset(of: c)
결과

false
false

c의 모든 원소가 a에 존재하지 않으므로 isSuperset의 결과는 false이다.
a가 c의 상위집합이 아니므로 진상 위집합이 될 수 없다. 따라서 isStrictSuperset의 결과는 false이다.

var a: Set = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var b: Set = [1, 3, 5, 7, 9]
var c: Set = [2, 4, 6, 8, 10]
let d: Set = [1, 7, 5, 9, 3]

a.isDisjoint(with: b) //교집합이 있어서 거짓
a.isDisjoint(with: c) //교집합이 있어서 거짓
b.isDisjoint(with: c) //교집합이 없어서 참
결과

false
false
true
서로소
A ∩ B = ∅

a와 b는 교집합이 존재하므로 서로소 관계가 아니다. 따라서 서로소 관계를 판단하는 isDisjoint method의 결과는 false이다.
a와 c는 교집합이 존재해 서로소 관계가 아니므로 isDisjoint의 결과는 false이다.
b와 c는 교집합이 존재하지 않아 서로소 관계이다 따라서 isDisjoint의 결과는 true이다.

Combining Sets (집합연산)

a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
b = [1, 3, 5, 7, 9]
c = [2, 4, 6, 8, 10]

var result = b.union(c)
결과

{5, 6, 7, 10, 9, 4, 8, 2, 1, 3}
합집합
A ∪ B
A 혹은 B에 속하는 원소를 중복 없이 담은 집합.

uinon method는 합집합을 의미한다.
두 집합 중 한 번이라도 포함된 원소를 중복 없이 담은 집합으로 새로운 set을 반환한다.
이때 set의 특징인 순서가 없는 컬렉션임에 유의하자.
이와 반대로 새 set을 반환하는 것이 아닌 기존의 set에 덮어 씌워버리는 method도 존재한다.

a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
b = [1, 3, 5, 7, 9]
c = [2, 4, 6, 8, 10]

var result = b.union(c)

//-----

b
b.formUnion(c)
b
결과

{3, 5, 9, 7, 1}
{4, 6, 3, 5, 9, 2, 10, 8, 7, 1}
{4, 6, 3, 5, 9, 2, 10, 8, 7, 1}

b 자체가 c와의 합집합이 된 것을 볼 수 있다.

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var b = [1, 3, 5, 7, 9]
var c = [2, 4, 6, 8, 10]

result = a.intersection(b)
result = c.intersection(b)
결과

{3, 1, 5, 9, 7}
Set([])
교집합
A ∩ B
A와 B에 모두 존재하는 원소를 모은 집합

intersection method는 두 집합이 공유하고 있는 원소들을 모아 새로운 set으로 반환한다.
마찬가지로 formIntersection method는 새 set을 반환하는 대신 교집합을 제외한 모든 요소를 삭제한다.

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var b = [1, 3, 5, 7, 9]
var c = [2, 4, 6, 8, 10]

a
a.formIntersection(b)
a
결과

{3, 2, 4, 7, 6, 8, 1, 5, 9}
{3, 1, 5, 9, 7}
{3, 1, 5, 9, 7}

만약 교집합이 존재하지 않는다면 요소들을 전부 삭제한다.

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var b = [1, 3, 5, 7, 9]
var c = [2, 4, 6, 8, 10]

result = a.symmetricDifference(b)
result = c.symmetricDifference(b)
결과

{8, 2, 6, 4}
{2, 8, 1, 4, 7, 6, 9, 3, 10, 5}
대칭차
A ∆ B
둘 중 한 집합에는 속하지만 둘 모두에는 속하지는 않는 원소들의 집합

symmetricDfference method는 겹치지 않는 원소들로 만든 새 set을 반환하는 method이다.
따라서 a와 b에 모두 존재하는 원소들을 제외한 {8, 2, 6, 4} 반환하고,
c와 b에 모두 존재하는 원소를 제외한 {2, 8, 1, 4, 7, 6, 9, 3, 10, 5}를 반환한다.

마찬가지로 새 set을 반환하는 것이 아닌, set 자체를 덮어버릴 수 있다.

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var b = [1, 3, 5, 7, 9]
var c = [2, 4, 6, 8, 10]

a
a.formSymmetricDifference(b)
a
결과

{8, 1, 2, 6, 4, 7, 9, 3, 5}
{8, 2, 6, 4}
{8, 2, 6, 4}

원래의 a의 형태와 달라진 것을 볼 수 있다.

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var b = [1, 3, 5, 7, 9]
var c = [2, 4, 6, 8, 10]

result = a.subtracting(b)
결과

{6, 2, 8, 4}
차집합
A - B
A에는 속하지만 B에는 속하지 않는 원소들의 집합

차집합은 A에 존재하는 원소들 중 B에 존재하는 원소들을 제외한 원소들의 집합이다.
따라서 a에 존재하는 원소들 중 b에도 혼재한 [1, 3, 5, 7, 9]가 삭제된 새 set이 반환된 것을 확인할 수 있다.
method 이름에 ing가 붙었으므로 subtracting이 아닌 subtarct를 사용하면 원본 set 자체를 수정하게 된다.

 

lterating Collection (콜렉션 열거하기)

컬렉션의 요소를 열거하는 방법이다.
반복문을 통해 수행할 수 있으며, 가장 간단한 방법은 for-in 반복분을 사용하는 것이다.

Syntax

for element in collection {
    statements
}

 

print("Array", "===================")
let arr = [1, 2, 3]
for num in arr {
    print(num)
}
결과

Array ===================
1
2
3

컬렉션을 전달하면 저장된 요소의 수만큼 반복하게 된다.
셋과 딕셔너리도 동일하게 열거할 수 있지만 순서가 없는 컬렉션임에 주의하자.

print("Set", "==================")
let set: Set = [1, 2, 3]
for num in set {
	print(num)
}

print("Dictionary", "======================")
let dict = ["A": 1, "B": 2, "C": 3]
for (elem) in dict {
	print(elem.key, elem.value)
}
결과

Set ==================
1
2
3
Dictionary ======================
C 3
A 1
B 2

또 다른 방법은 forEach method를 사용하는 것이다.
forEach method는 함수형 프로그래밍에서 자주 사용하며, 열거할 컬렉션을 클로저 형태로 전달한다는 점이 특별하다.

let arr = [1, 2, 3]
arr.forEach { (num) in
	print(num)
}

print("Set", "====================")
let set: Set = [1, 2, 3]
set.forEach { (num) in
	print(num)
}

print("Dictionary", "===============")
let dict = ["A": 1, "B": 2, "C": 3]

dict.forEach { (elem) in
	print(elem.key, elem.value)
}
결과

Array =====================
1
2
3
Set ====================
1
3
2
Dictionary ===============
B 2
A 1
C 3

같은 결과를 얻었지만 두 방법에는 약간의 차이점이 존재한다.

func withForIn() {
	print(#function)
	let arr = [1, 2, 3]
	for num in arr {
		print(num)
	return
	}
}
func withForEach() {
	print(#function)
	let arr = [1, 2, 3]
	arr.forEach { (num) in
		print(num)
		return
	}
}

withForIn()
withForEach()
결과

withForIn()
1
withForEach()
1
2
3

for-in 반복문을 사용한 열거의 경우 반복문의 특징을 살려 break와 continue를 사용할 수 있다.
하지만 method형태의 forEach의 경우 이를 사용할 수 없다.
또한 return의 역할도 상이한데 for-in 내의 return은 해당 블록을 바로 종료해 결과가 1만 출력됐지만,
forEach의 return은 함수 실행이 아닌 클로저의 동작을 중지하기 때문에
함수의 동작에 영향 없이 모든 요소가 출력된 것을 확인할 수 있다.

 

KeyValuePairs (키밸류페어)

keyvaluepairs는 딕셔너리와 마찬가지로 key와 value를 쌍을 이루어 저장하는 컬렉션이다.
딕셔너리와 비슷하지만 배열의 특징을 가지고 있는 컬렉션이다.

let words = ["A": "Apple", "B": "Banana", "C": "City"]
let words2: KeyValuePairs<String, String> = ["A": "Apple", "B": "Banana", "C": "City"]
let words3: KeyValuePairs = ["A": "Apple", "B": "Banana", "C": "City"]

 

형식 추론 방식으로는 딕셔너리와 키 밸류 페어 둘을 구별하지 못한다.
반드시 명시적으로 선언해 줘야 한다.

  1. 딕셔너리는 유일한 key를 중복해서 요소를 저장할 수 없지만 키 밸류 페어는 가능하다.
  2. 딕셔너리는 정렬되지 않지만 키밸류페어는 정렬이 가능하다.
  3. 딕셔너리보다 느리지만 요소가 정말 많아지지 않는다면 큰 차이는 없다.
let words3: KeyValuePairs = ["A": "Apple", "B": "Banana", "C": "City"]

words3.count
words3.isEmpty
결과

3
false

다른 컬렉션과 마찬가지로 count 속성과 isEmpty 속성을 지원한다.

words3.contains(where: ((key: String, value: String)) throws -> Bool)
words3.first(where: ((key: String, value: String)) throws -> Bool)
words3.firstIndex(where: ((key: String, value: String)) throws -> Bool)

요소를 검색할 때 쓰는 contains, 특정 요소를 검색하는 first나 last, 인덱스를 검색하는 firstIndex 또한 사용 가능하다.

let words3: KeyValuePairs = ["A": "Apple", "B": "Banana", "C": "City"]

words3[0]
words3[0].key
words3[0].value
결과

(key "A", value "Apple")
"A"
"Apple"

키 밸류 페어에서는 딕셔너리와는 다르게 key로 접근하는 것이 아닌 index를 통해 요소에 접근한다.

다 좋아 보이지만 키 밸류 페어는 단점이 존재한다.

  1. append, insert, update, remove의 컬렉션 수정을 지원하지 않는다.
  2. 위의 이유 때문에 검색 기능을 제외하면 기능이 빈약해 parameter 전달용이나 임시 값 저장용으로 사용한다.
  3. key와 value를 쌍으로 저장하고, 동일한 값을 여러 번 저장해야 하며, 저장되는 순서가 중요하면서 동일한 순서로 여러 번 처리해야 한다면 제한적으로 사용할 수 있다.

 


Log

2021.09.05.
블로그 이전으로 인한 글 옮김 및 수정