집합(Set)은 같은 타입의 서로 다른 값을 중복없이 저장하고자 할 때 사용하는 집단 자료형이다. 순서가 중요하지 않은 데이터들이거나 중복 없이 한 번만 저장되어야 하는 데이터들을 다룰 때 배열 대신 사용할 수 있는 자료형이다.
집합의 정의
집합을 정의할 때는 배열과 마찬가지로 초기값을 사용해 바로 정의하거나 빈 집합을 선언하고 초기화 과정을 거쳐 정의하는 즉 정적인 방법, 동적인 방법 두 가지가 존재한다.
// 초기값 사용해 바로정의(정적)
var language : Set = ["Korean", "English", "Chinese"]
// 초기값 사용하지 않은 빈 집합 정의(동적)
var language = Set<String>()
// 아이템 추가
language.insert("Korean")
language.insert("English")
language.insert("Chinese")
집합에서는 아이템을 추가할 때 insert(_:) 메소드를 사용한다.
집합에선 count 속성과 isEmpty 속성을 잘 사용하면 집합이 비었는지 안 비었는지를 잘 확인할 수 있는데 집합은 중복된 데이터를 허용하지 않기에 배열과 길이가 다를 수 있다는 점에 주의해야한다.\
var language = Set<String>()
language.insert("Korean")
language.insert("English")
language.insert("Chinese")
if language.isEmpty {
print("집합이 비어있습니다")
} else {
print("집합에 현재 \(language.count)개의 아이템이 저장되어 있습니다")
}
/* 실행결과
집합에는 현재 3개의 아이템이 저장되어 있습니다
집합 순회 탐색
for ~ in 구문을 사용하면 집합도 배열처럼 순회 탐색을 할 수 있다. 다만 배열처럼 인덱스를 사용해 순회 탐색은 할 수 없고, 순회 속성을 이용해 집합 자체를 for ~ in 구문에 넣고 순회처리를 하면 된다.
var languages : Set = ["Korean", "English", "Chinese"]
for l in languages {
print("\(l)")
}
/* 실행결과
English
Chinese
Korean
집합에는 순서가 따로 없으므로 실행결과는 다음과 같이 출력되게 된다. 만약 순서(알파벳)대로 결과를 얻고 싶다면 sort() 메소드를 사용하면 된다.
...(중략)...
for l in languages.sorted() {
print("\(l)")
}
/* 실행결과
Chinese
English
Korean
집합의 동적 추가와 삭제
집합에 아이템을 추가할 때는 insert(_:) 메소드를 사용하면된다. 이 메소드를 사용하면 아이템을 집합에 추가하지만, 집합의 특성상 중복이 되지 않으므로 이미 존재하는 아이템을 추가하게되면 아무 처리도 하지 않게된다.
var languages : Set = ["Korean", "English", "Chinese"]
languages.insert("Japanese")
// languages = ["Japanese", "Korean", "Englsih", "Chinese]
languages.insert("Chinese")
// languages = ["Japanese", "Korean", "Englsih", "Chinese]
languages.insert("Korean")
// languages = ["Japanese", "Korean", "Englsih", "Chinese]
예제에서 보이는 것 처럼 Korean, Chinese의 경우 이미 집합내에 존재하기 때문에 추가되지 않는 모습을 볼 수 있다.
집합의 아이템을 삭제할 때는 remove(_:) 메소드를 사용한다. 다만 삭제할 값이 집합내에 존재하지 않으면 아무것도 삭제하지 않고 nil값을 반환한다.
...(중략)...
if let removedItem = languages.remove("Japanese") {
print("아이템 \(removedItem)의 삭제가 완료되었습니다")
} else {
print("삭제할 값이 집합에 추가되어 있지 않습니다")
}
/* 실행결과
아이템 Japanese의 삭제가 완료되었습니다
개별 아이템을 삭제하는 remove(_:) 메소드 외에 스위프트는 집합 내에 모든 아이템을 삭제할 수 있는 메소드 removeAll()도 제공한다. 이 메소드는 인자값 없이 호출되어 해당 집합의 모든 아이템을 일괄 삭제한다.
...(중략)...
languages.removeAll() // languages 집합의 모든 아이템 삭제
if languages.isEmpty {
print("모든 아이템이 삭제되었습니다")
} else {
print("아직 \(languages.count)개의 아이템이 남아있습니다")
}
/* 실행결과
모든 아이템이 삭제되었습니다
그 외에도 스위프트는 집합에 특정 아이템이 있는지를 확인할 수 있게 해주는 contains(_:) 메소드를 제공한다. 일치하는 아이템이 있으면 ture, 없으면 false를 반환한다.
var languages : Set = ["Korean", "English", "Chinese"]
if languages.contains("Korean") {
print("Korean 아이템이 저장되어 있습니다")
} else {
print("Korean 아이템이 저장되어 있지 않습니다")
}
/* 실행결과
Korean 아이템이 저장되어 있습니다
집합연산
집합 자료형은 수학에서 배웠던 집합의 개념과 거의 동일하다. 이 때문에 집합 자료형끼리는 집합연산을 할 수 있다. 스위프트에선 집합끼리의 연산을 쉽게 처리할 수 있도록 여러가지 메소드를 제공한다.
✅ 기본 집합 연산
집합 연산의 결과값은 집합으로 구성되는데, 다음은 두 개의 집합 a, b의 연산과 그 결과를 표현하는 그림이다.
그림에서 표현하는 연산과 각 메소드는 모두 4개로 각각에 대한 설명은 다음과 같다.
- intersection(_:) : a, b 집합에서 공통인 부분을 의미한다. 수학의 교집합과 같다.
- symmetricDifference(_:) : a, b 집합에서 공통인 교집합을 제외한 집합을 의미한다.
- union(_:) : a, b 집합 모두를 의미한다. 수학의 합집합과 같다.
- subtract(_:) : 한 쪽 집합에 있는 모든 아이템에서 다른 쪽 집합에도 속하는 공통 아이템을 제외하고 새로운 집합을 만들어주는 메소드이다. 수학의 차집합과 같다.
이 중에서 마지막 subtract(_:) 메소드를 제외하면 나머지 메소드는 모두 양쪽 집합의 위치가 바뀌더라도 결과 값이 동일하다.
var oddDigits : Set = [1, 3, 5, 7, 9] // 홀수집합
var evenDigits : Set = [0, 2, 4, 6, 8] // 짝수집합
let primeDigits : Set = [2, 3, 5, 7 ] // 소수집밥
oddDigits.intersection(evenDigits).sorted()
// [ ]
oddDigits.symmetricDifference(primeDigits).sorted()
// [1, 2, 9]
oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.subtract(primeDigits)
oddDigits.sorted()
// [1, 9]
subtract(_:) 메소드는 위의 세 메소드와 다르게 새로운 집합을 만들어 내지 않는다. 단지 대상 집합에서 아이템을 직접 제거할 뿐이다. 즉, 대상 집합의 내용을 직접 변경한다는 의미이다.
✅ 부분집합과 포함관계 판단 연산
부분집합의 개념이 우리가 배웠던 수학과 프로그래밍 사이에선 조금 차이가 있다. 프로그래밍 관점으로 말하자면, 집합 A와 B의 아이템이 모두 일치할 때 A == B가 성립하며, 이와 동시에 두 집합은 서로의 부분집합이 될 수 있다.
스위프트에서는 집합 자료형에 대해 부분집합 관계를 확인해주는 메소드를 제공하는데, 대표적으로 다음과 같은 5개의 메소드가 사용된다.
- isSubset(of:) : 주어진 집합의 값 전체가 특정 집합에 포함되는지 판단해 true, false 반환
- isSuperset(of:) : 주어진 집합이 특정 집합의 모든 값을 포함하는지 판단해 true, false 반환
- isStrictSubset(of:)와 isStrictSuperset(of:) : 프로그래밍 적으로 A == B가 성립하면 A는 B의 부분집합이 될 수도있고 B는 A의 부분집합이 될 수도 있다. 하지만 이 메소드는 이를 엄격하게 관리해 우리가 수학에서 풀었던 개념과 똑같이 부분집합, 포함된 집합 관계를 판단해준다. 즉 A == B의 경우 서로 부분집합이 될 수 없다는 것이다.
- isDisjoint(with:) : 두 집합 사이의 공통 값을 확인해 공통값이 없으면 ture, 있으면 false를 반환한다.
다음은 이 메소드들을 활용한 집합 사이의 포함관계를 판단해본 예제이다.
let A : Set = [1, 3, 5, 7, 9]
let B : Set = [3, 5]
let C : Set = [3, 5]
let D : Set = [2, 4, 6]
B.isSubset(of: A) // true
A.isSuperset(of: B) // true
C.isStrictSubset(of: A) // true
C.isStrictSubset(of: B) // false
A.isDisjoint(with: D) // true
집합은 배열과 매우 유사한 자료형이지만 순서나 인덱스가 없고 중복된 아이템을 허용하지 않는다는 점에서 차이가 있다. 그래서 배열에서 중복된 값을 찾아 없애고 싶으면 중복 부분을 직접확인하는 것보다 배열 데이터를 집합 데이터로 변환한 후에 다시 배열로 변환하면 훨씬 쉽게 중복값들을 제거할 수 있다.
var A = [ 4, 2, 5, 1, 7, 4, 9, 11, 3, 5, 4] // 배열
let B = Set(A) // 집합
A = Array(B) // 중복이 제거된 배열
// [2, 4, 9, 5, 7, 3, 1, 11]
// 한 줄로도 가능
A = Array(Set(A))
이렇게 집합 구조체가 갖는 특성을 이용해 중복 데이터를 제거하는 것은, 집합을 전체 순회하면서 중복값이 있는지 판단하고 삭제하는 구문에 비해 훨씬 효율적인 방법이다.
출처 : 꼼꼼한 재은씨의 Swift 문법편
'Swift > 문법' 카테고리의 다른 글
스위프트(Swift) - 집단자료형(Collection Types) Ⅳ. 딕셔너리(Dictionary) (0) | 2022.05.10 |
---|---|
스위프트(Swift) - 집단 자료형(Collection Types) Ⅲ. 튜플(Tuple) (0) | 2022.05.09 |
스위프트(Swift) - 집단 자료형(Collection Types) Ⅰ. 배열(Array) (0) | 2022.05.05 |
스위프트(Swift) - 제어 전달문(Control Transfer Statements) (0) | 2022.05.04 |
스위프트(Swift) - 조건문 Ⅳ. switch 구문 (0) | 2022.05.03 |