본문 바로가기

스피드 문법정리

12. Collection

Collection

데이터 모음을 효율적으로 처리하기 위해서 사용하는 특별한 자료형

  설명 예시
Foundation Collection 콜랙션을 참조 형식으로 사용해야할 때
객채형식의 데이터만 저장 가능
모든 자료형 가능
NSArray, NSDictionary, NSSet
Swift Collection 콜랙션을 값 형식으로 사용해야할 때
객체형식, 값 형식 모두 가능
동일한 자료형만 저장 가능
Array, Dictionary, Set

Copy - on - write

Swift Collection은 값 형식으로 반드시 복사가 필요한 경우만 복사를 실행함, 콜랙션이 변경되지 않는다면 항상 동일한 데이터 사용, 특정 시점에 콜랙션을 변경한다면 그 시점에 복사본을 생성하고 변경사항 적용

Array

데이터를 순서대로 저장하는 콜랙션, 배열은 순서에 따라 index 번호가 부여되는데 배열의 마지막을 추가, 삭제 할 경우 큰 문제가 없지만 배열의 중간에서 추가, 삭제를 할 경우 뒤에 있는 요소들이 이동해야 되기 때문에 오버헤드 발생

// Array Literal
[elem, elem, ...]

// Array Type
Array<T>
[T]

Creating an Array

let nums = [1, 2, 3]
let emptyArray : [Int] = []
let emptyArray2 : Array<Int>()
let emptyArray3 : [Int]()
let zeroArray = [Int](repeating : 0, count : 10)

Inspecting an Array

let nums = [1, 2, 3]

nums.count // 3
nums.count == 0 // false
nums.isEmpty // false

Accessing Elements

배열에 접근할 때 배열의 갯수, 순서 등에 잘못 접근하면 오류가 발생하기 때문에 서브 스크립트 문법보다 프로퍼티로 접근하는 게 더 안전함

let fruits = ["Apple", "Banana", "Melon"]
fruits[0] // "Apple"
fruits[0...1] // "Apple", "Banana"


fruits[fruits.startIndex] // "Apple"
fruits[fruits.index(before : fruits.endIndex)] // "Melon"
fruits.first // "Apple"
fruits.last // "Melon"

Adding Elements

insert의 경우 중간에 배열을 추가할 경우 오버헤드 발생하기 때문에 append로 맨 앞이나 맨 뒤에 추가하는것을 권장

var alphabet = ["A", "B", "C"]
alphabet.append("E") //["A", "B", "C", "E"]
alphabet.append(contentsOf : ["F", "G"]) // ["A", "B", "C", "E", "F", "G"]
alphabet.insert("D", at : 3) // ["A", "B", "C", "D", "E", "F", "G"]
alphabet.insert(contentsOf : ["a", "b", "c"], at : 0) // ["a", "b", "c", "A", "B", "C", "D", "E", "F", "G"]
alphabet.[0...2] = ["x", "y", "z"]  // ["x", "y", "z", "A", "B", "C", "D", "E", "F", "G"]
alphabet.replaceSubrange(0...2, with : ["a", "b", "c"]) // ["a", "b", "c", "A", "B", "C", "D", "E", "F", "G"]
alphabet.[0...2] = ["z"] // ["z", "A", "B", "C", "D", "E", "F", "G"]
alphabet.[0..<1] = [] // ["A", "B", "C", "D", "E", "F", "G"]

Removing Elements

var alphabet = ["A", "B", "C", "D", "E", "F", "G"]
alphabet.remove(at : 2) // "C"
alphabet // ["A", "B", "D", "E", "F", "G"]
alphabet.removeFirst() // ["A"]
alphabet // ["B", "D", "E", "F", "G"]
alphabet.removeFirst(2) // return X
alphabet // ["E", "F", "G"]
alphabet.removeAll() // []
alphabet.popLast() // nil

var alphabet = ["A", "B", "C", "D", "E", "F", "G"]
alphabet.popLast() // "G"
alphabet // ["A", "B", "C", "D", "E", "F"]
alphabet.removeSubrange(0...2) // ["D", "E", "F"]
alphabet.[0...2] = [] // ["D", "E", "F"]
alphabet // []

Comparing Arrays

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

a == b // false
a != b // true

a.elementsEqual(b) // false
a.elementsEqual(b) {(lhs, rhs) -> Bool in
    return lhs.caseInsensitiveCompare(rhs) == .orderedSame //true

Finding Arrays

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

nums.contains(1) // true
nums.contains(8) // false

nums.contain { (n) -> Bool in // true
    return n % 2 == 0
}

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

nums.firstIndex { (n) -> Bool in // 1
    return n % 2 == 0
}

nums.firstIndex(of : 1) // 0

nums.lastIndex(of : 1) //3

Sorting on Arrays

sort -> 배열을 직접 정렬, 새로운 배열 리턴 X

sorted 정렬된 새로운 배열을 리턴

let nums = [1, 2, 3, 1, 4, 5, 2, 6, 7, 5, 0]
nums.sorted() // [0, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7]
nums //  [1, 2, 3, 1, 4, 5, 2, 6, 7, 5, 0]

nums.sorted{ (a, b) -> Bool in // [7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 0]
    return a > b
}
nums.sorted().reversed()
[Int](nums.sorted().reversed())  // [7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 0]

var mutableNums = nums // [1, 2, 3, 1, 4, 5, 2, 6, 7, 5, 0]
mutableNums.sort() // [0, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7]
mutableNums.reverse() // [7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 0]

mutableNums // [7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 0]
mutableNums.swapAt(0, 1) // [6, 7, 5, 5, 4, 3, 2, 2, 1, 1, 0]

mutableNums.shuffle() // 요소의 순서 랜덤 변환
mutableNums.shuffled() // 요소의 순서 랜덤 변환 후 리턴

Dictionary

키와 값을 하나의 쌍으로 저장하는 콜랙션, 키는 유일한 값을 가져야 함, 정렬하지 않는 콜랙션

[key : value, key : value, ...]
var dict = ["A" : "Apple", "B" : "Banana"]
dict = [:]

Dictionary Type

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

Creating a Dictionary

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

let emptyDict : [String : String] = []
let emptyDict2 = [String : String]()
let emptyDict3 = Dictionary<String : String>()

Inspecting a Dictionary

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

words.count // 3
words.isEmpty // false

Accessign Keys and Values

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

words["A"] // "Apple"
words["Apple"] // nil

let a = words["E"] // nil
let b = words["E", default : "Empty"] //Empty

for k in words.keys.sortecd() // A, B, C
    print(k)
}

for v in words.value { // Banana, Apple, City
    print(v)
}

let keys = Array(words.keys)
let values = Array(words.values)

Adding Keys and Values

var words = [String : String]()

words["A"] = "Apple" // "A" : "Apple"
words["B"] = "Banana" // "A" : "Apple", "B" : "Banana"

words["B"] = "Ball" // "A" : "Apple", "B" : "Ball"

words.updateValue("City", forKey : "C") // nil / "A" : "Apple", "B" : "Ball",  "C" : "City"
words.updateValue("Circle", forKey : "C") // City / 이전에 저장된 값 리턴

만약 기존에 값이 없었다면 추가 insert
기존에 값이 있다면 업데이트 update
insert + update = upsert

Removing Keys and Values

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

words["B"] = nil // ["A" : "Apple", "C" : "Circle"]
words["Z"] = nil // nil // 존재하지 않는 코드일 경우 다음 코드 실행

words.removeValue(forKey : "A") // "Apple" / 삭제되는 값 리턴
words.removeValue(forKey : "A") // nil /  존재하지 않는 코드일 경우 다음 코드 실행

words.removeAll() // 전체 삭제

Comparing Dictionaries

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

a == b // true
a != b // false

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

a == b // false // 대소문자도 구별
a != b // true

a.elemetsEqual(b) {(lhs, rhs) -> Bool in 
    return lhs.key.caseInsensitiveCompare(rhs.key) == .orderSame && 
           lhs.value.caseInsensitiveCompare(rhs.key) == .orderSame
} // true 혹은 false를 리턴, 딕셔너리는 정렬되지 않은 배열이기 때문에 코드를 실행할 때 마다
  // 다른 배열형태로 전달되기 때문에 ture 혹은 false로 리턴
 
let aKeys = a.keys.sorted()
let bKeys = b.keys.sorted()

aKeys.elementsEqual(bkeys) { (lhs, rhs) -> Bool in // true
    guard lhs.key.caseInsensitiveCompare(rhs) == .orderSame else {
        return false
    }
    guard let lv = a[lhs], let rv = b[rhs], lv.caseInsensitiveCompare(rhs) == .orderSame else {
        return false
    }
    return true
}

Finding Elemets

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

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

words.contains(where : c) //true

let r = words.first(where : c)
r?.key
r?.value
// 처음으로 검색된 결과를 리턴, 하지만 딕셔너리는 정렬되지 않는 배열이기 때문에 값이 매번 달라짐

words.filter(c) // ["C" : "City", "B" : "Banana"]
// 조건을 만족시키는 값이 새로운 딕셔너리에 배열됨

Set

집합 연산을 제공하는 콜랙션, 정렬순서보다 검색속도가 중요한 경우 사용, 배열처럼 하나의 데이터를 하나의 요소로 저장, 정렬되지 않고 인덱스도 사용하지 않음, 동일한 값을 하나만 저장

Set Type

Set<T>

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

InspectingType

let set : Set<Int> = [1, 2, 2, 3, 3, 3] // 1, 2, 3

set.count // 3
set.isEmpty // false

Testing for Membership

let set : Set<Int> = [1, 2, 2, 3, 3, 3] // 1, 2, 3

set.contains(1) // true

Adding and Removing Elements

var words = Set<String>()

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

// Set은 중복을 허용하지 않음
var insertResult = words.insert("Swift")
insertResult.inserted // false
insertResult.memberAfterInsert // "Swift"

// Upsert 방식 사용
var updateResult = words.update(with : "Swift") // "Swift"
updateResult  // "Swift"
var updateResult = words.update(with : "Apple") // nil
updateResult  // "nil

words.remove("Swift")
words.removeAll()

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]
var d : Set = [1, 7, 5, 9, 3]

a == b // false	
a != b // true
b == d // true

a.isSubset(of : a) //true
a.isStrictSubset(of : a) //false

b.isSubset(of : a) //true
b.isStrictSubset(of : a) //true

a.isSuperset(of : a) //true
a.isStrictSuperset(of : a) //false

a.isSuperset(of : b) //true
a.isStrictSuperset(of : b) //true

a.isSuperset(of : c) //false
a.isStrictSuperset(of : c) //false

a.isDisjoint(with : b) // false 교집합O
a.isDisjoint(with : c) // false 교집합O
a.isDisjoint(with : c) // true 교집합x

Combining Sets

합집합은 union을 사용하고 두 변수를 합친 후에 새로운 Set을 리턴, formUnion은 원본을 변경

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) // 3, 4, 10, 6, 1, 9, 8, 2, 5, 7
result = b.union(a) // 3, 6, 4, 1, 9, 8, 2, 5, 7

// form은 원본을 변경
b.formUnion(c) // 3, 4, 10, 6, 1, 9, 8, 2, 5, 7
b // 3, 4, 10, 6, 1, 9, 8, 2, 5, 7

 

교집합은 intersection을 사용하고 두 변수의 공통요소를 새로운 Set을 리턴, formIntersection은 원본을 변경

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

result = a.intersection(b) // 1, 5, 3, 9, 7
result = c.intersection(b) // Set[]

a.formIntersection(b) // 1, 5, 3, 9, 7
a // 1, 5, 3, 9, 7

b.formIntersection(c) // Set[]

여집합은 symmetricDifference을 사용하고 새로운 Set을 리턴, formsymmetricDifference는 원본을 변경

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

result = a.symmetricDifference(b) // 6, 8, 2, 4
result = a.symmetricDifference(b) // 3, 10, 6, 7, 1, 9, 8, 2, 4, 5

result = a.formsymmetricDifference(b) // 6, 8, 2, 4
a // 6, 8, 2, 4

차집합은 subtracting을 사용하고 새로운 Set을 리턴, subtract 원본을 변경

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

reuslt = a.subtracting(b) // 6, 8, 2, 4

a.subtract(b) // 6, 8, 2, 4

Iterating Collections

컬랙션을 열거하는 방법

for in 

for element in collection {
    statements
}
// Array 열거
let arr = [1, 2, 3]
for num in arr {
    print(num)
}
// 1, 2, 3

// Set 열거
let set : Set = [1, 2, 3]
for num in set {
    print(num)
}
// 1, 3, 2 / 정렬하지 않기 떄문에 결과의 순서가 매번 다름

// Dictionary 열거
let dict = ["A" : 1, "B" : 2, "C" : 3]
for (key, value) in dict {
    print(key, value)
}
// C 3, A 1, B 2 / 정렬하지 않기 떄문에 결과의 순서가 매번 다름

forEach

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

// Set 열거
let set : Set = [1, 2, 3]
set.forEach { (num) in
    print(num)
}
// 1, 3, 2 / 정렬하지 않기 떄문에 결과의 순서가 매번 다름
    
// Dictionary 열거
let dict = ["A" : 1, "B" : 2, "C" : 3]
dict.forEach { (elem) in
    print(elem.key, elem.value)
}
// C 3, A 1, B 2 / 정렬하지 않기 떄문에 결과의 순서가 매번 다름

for in vs forEach

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

func withForEach() {
    print(#function)
    let arr = [1, 2, 3]
    arr.forEach { (num) in
       print(num)
       return
    }
}
// 1, 2, 3
for in forEach
반복문으로 break, continue 사용 가능 매서드로 break, continue 사용 불가능
return 동작방식이 반복문 코드문이 바로 종료 forEach 클로저로 전달한 클로저 내부에서 호출한 리턴문은 외부에는 영향을 주지 않고 반복횟수에도 영향을 주지 않고 지금 실행하고 있는 클로저 코드에만 영향을 줌

 

'스피드 문법정리' 카테고리의 다른 글

14. Structure and Class  (0) 2020.06.18
13. Enumeration  (0) 2020.06.17
9. Functions  (0) 2020.06.15
7. Control Transfer Statements, Labeled Statements  (0) 2020.06.12
6. Loop Statements  (0) 2020.06.12