[TIL]04.24

rbw·2022년 4월 24일
0

TIL

목록 보기
10/98
post-thumbnail

Collection

Swift에서는 Foundation Collection과 Swift Collection이 존재한다. 스위프트 컬렉션은 구조체로 이루어 져있으며 값타입이다. 그리고 하나의 타입들로만 구성할 수 있고, 객체와 값을 전부 저장이 가능하다.

Foundation Collection은 클래스로 이루어져있으며, 참조형식이다. 내부 저장할 요소가 다른 타입이여도 상관이없다. 하지만 객체형식으로만 저장이 가능하다. 이름으로는 접두어에 NS가 붙는 차이가 있다. 가변 컬렉션으로 만드려면 접두어에 NSMutable을 붙여주면 된다.

배열 요소의 접근

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

// first, last는 값이 없다면 nil을 반환하는 옵셔널 타입이다. 좀 더 안전하다
fruits.first // apple
fruits.last // cherry

fruits[fruits.index(before:fruits.endIndex)] // cherry

배열 요소 추가

var alphabet = ["A", "B", "C"]

// 마지막 요소에 추가한다.
alphabet.append("E")
alphabet.append(contentsOf: ["F", "G"])

// 특정 위치에 추가한다.
alphabet.insert("D", at: 3)
alphabet.insert(contentsOf: ["a", "b" ,"c"], at: 0)

// 특정 범위의 배열 요소를 변경하는 코드
alphabet[0...2] = ["x", "y", "z"]

alphabet[..<1] = [] // 멘 앞의 요소가 삭제가된다.

배열 요소의 삭제

// 특정 인덱스에 요소를 제거하고 리턴함
alphabet.remove(at: 2)

// 처음 요소를 제거하고 리턴함
alphabet.removeFirst()

// 앞 두개를 제거하고 리턴은 하지 않는다
alphabet.removeFirst(2)

// 전부 삭제한다
alphabet.removeAll

// 좀 더 안전한 방식의 제거, 옵셔널 타입을 반환
alphabet.popLast()

// 특정 범위의 요소를 제거
alphabet[0...2] = [] 

배열의 비교

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

// 같은지 판단
a.elementsEqual(b)

// 대소문자 구별 x 
a.elementsEqual(b) { (lhs, rhs) -> Bool in
    return lhs.caseInsesitiveCompare(rhs) == .orderedSame
}

// 축약하면
a.elementsEqual(b) {
    $0.caseInsensitiveCompare($1) == .orderedSame
}

배열 요소의 검색

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

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

// 짝수임을 확인하는 코드, 클로저로 전달하였다.
nums.contains { (n) -> Bool in
    return n % 2 == 0
}

// 처음 나온 짝수 요소의 값을 리턴한다. 여기서는 2
nums.first { (n) -> Bool in
    return n % 2 == 0
}

// 처음 나온 짝수의 인덱스를 리턴함. 여기서는 1
// 이 메소드는 값을 전달도 가능하다. e.g. (of: 1) -> 0
nums.firstIndex { (n) -> Bool in
    return n % 2 == 0
}

배열의 정렬

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

let sortedNums = nums.sorted() 

// 내림차순 정렬 구현
nums.sorted { (a, b) -> Bool in
    return a > b 
}

// 배열 생성자를 사용해서 생성해줌, 역순
[Int]nums.sorted().reveresed()

// 특정 인덱스 끼리의 순서를 바꾸는 메소드
mutableNums.swapAt(0, 1)

Dictionary의 요소 검색

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

// 먼저 파라미터로 튜플을 받고 불린형을 리턴하는 클로저를 구현해야한다
// 키의 요소에 "B" 가 있거나 값의 i 가 포함되면 true
let c: ((String, String)) -> Bool = {
    $0.0 == "B" || $0.1.contains("i")
}
words.contains(where: c) // true

Set

집합의 연산에 대해서 알아보겠습니다.

var a : Set = [1,2,3,4,5,6]
var b : Set = [1,3,5]
var c : Set = [10,20,30]

a.isSubset(of: a) // true 자기 자신에 대해 부분집합이므로 
a.isStrictSubset(of: a)// false 자기 자신은 진부분집합 x

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

// 교집합이면 false, 서로소 이면 true를 리턴한다.
a.isDisjoint(with: b) // false

// 합집합
var result = b.union(c)

// 원본을 변경하는 합집합
b.formUnion(c)

// 교집합
result = a.intersection(b)
b.intersection(a)

// 여집합
result = a.symmetricDifference(b)
a.symmetricDifference(b)

// 차집합
result = a.subtracting(b)

a.subtract(b) // 원본 변경

Iterating Collections

컬렉션을 열거하는 방법에대해 알아보겠습니다.

for-in을 활용하는 방법이 있습니다.

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

// 딕셔너리는 루프상수로 튜플을 받아야한다.
let dict = ["a" : 1, "b" : 2, "c" , 3]
for (key, value) in dict{ 
    print(key, value)
}

다음으로 forEach문입니다. 파라미터를 클로저로 받습니다.

// 위의 for-in과 같다
arr.forEach { (num) in 
    print(num)
}

// 튜플로 받는다.
dict.forEach { (tup) in
    print(tup.key, tup.value)
}

두 방식의 차이로는 함수로 사용할 시에 return문에서 차이가 있다. forEach 내부의 return은 외부에 영향을 주지않고 현재 실행중인 클로저만 종료하기 때문에 반복회수에는 영향이 없습니다.

하지만 for-in문 내부에 return은 외부에도 영향이 가기 때문에, 함수가 종료되고, 반복횟수에도 영향이 있습니다.

KeyValuePairs

딕셔너리와 배열의 특징을 가진 KeyValuePairs에 대해 알아보겠습니다.

특징으로는 정렬이 되어 있고, Hashable을 채용하지 않아서 Key 가 중복이 될 수 있지만, 검색속도가 느리다.

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

키와 값을 쌍으로 연결해야하고, 정렬 되어 있어야하고, 동일한 값을 저장하는 경우가 있다면 사용하면 좋을 듯하다.

Enumeration

열거형에 대해서 알아보겠습니다, 코드의 가독성과 안전성을 위해 주로 활용합니다. 이해하기 쉽게 열거형 케이스는 엄연히 값이라고 생각하면 쉽다.

// 문법은 다음과 같다
enum TypeName {
    case caseName
    case caseName1, caseName2
}

// 정렬 기능을 구현하는 예시
enum Alignment{
    case left
    case right
    case center 
}

Alignment.center 

var textAlignment = Alignment.center

// 이미 타입추론 가능하므로 열거형 을 생략함
textAlignmnet = .left

Raw Values

내부에 값을 저장하는 원시값에 대해 알아보겠습니다. 선언을 하면 나중에 바꿀수는 없습니다.

// 문법은 다음과 같습니다.
enum TypeName: RawValueType(String, Character, Number Types) {
    case caseName = Value 
}
 
// 0,1,2로 값이 적용된다. 값을 하나 저장하면 다음 값은 1을 더한 값이다.
enum Alignment: Int{
    case left
    case right
    case center 
}
Alignment.left.rawValue // 0

Alignment(rawValue: 0) // left를 리턴한다. 해당 값이 없다면 nil을 리턴

// 문자열로 타입을 선언하고 원시값을 설정하지 않으면 caseName이 값이된다.
// Character 타입은 원시값을 직접 저장해야만 한다.
enum Weekday: String {
    case sunday
    case monday = "Mon"
    ... 
}

Weekday.sunday.rawValue // "sunday" 
Weekday.monday.rawValue // "Mon"

Associated Values

연관값에 대해서 알아보고, 원시값이랑 차이를 알아보겠습니다.

연관값은 튜플로 선언한다.

// 해당 문법
enum TypeName {
    case caseName(Type)
    case caseName1(Type, Type, ...)
}

enum VideoInterface {
    case dvi(width: Int, height: Int)  
    case hdmi(Int, Int, Double, Bool)
    case displayPort(CGSize)
}

var input = VideoInterface.dvi(width: 2048, height: 1536)

switch input {
    case .dvi(2048, 1536) :
        print("dvi 2048 * 1536")
    case .dvi(2048, _) :
        print("dvi 2048 * Any")
    case .dvi :
        print("dvi")
    case .hdmi(let width, let height, let version, var audioEnabled) :
        print("hdmi \(width) * \(height)")

    // 모든 연관값을 동일한 형태로 바인딩 한다면 let 키워드를 앞에 표기도 가능하다. 
    // 이 패턴을 Enumeration Case Pattern 이라고 한다.
    case let .displayPort(size) :
        print("dp")
}

Enumeration Case Pattern

연관값을 가지는 열거형 케이스를 매칭하는 패턴에 대해서 알아보겠습니다.

// 다음 문법
case Enum.case(let name):
case var Enum.case(name):

enum Transprotation {
    case bus(number: Int)
    case subway(lineNumber: Int, express: Bool)
}

switch tpt {
    case .bus(let n) :
        print(n)
    case let subway(l, e):
        print(l, e)
}

// tpt가 2이면 바인딩이 되므로, if문 내부를 실행 한다.
// 앞의 let 키워드가 있으므로 express는 바인딩되어서 내부에서 상수로 사용가능하다.
// 바인딩을 하지 않을거라면, let키워드는 생략 가능
if case let .subway(2, express) = tpt {
    ...
}

// for where 사용 예시
// n은 루프상수로, where절을 활용해서 조건을 추가하였다
for case let .subway(n, true) in list where n == 2 {
    ...
}

CaseIterable

모든 케이스를 열거할수 있게 도와주는 프로토콜에 대해 알아보겠습니다.

// CaseIterable 프로토콜을 선언하면, 배열형태로 열거가 가능하다.
enum Weekday: Int, CaseIterable {
    case sunday
    case monday
    case tuesday
    case wednesday
    ...
}
// 하나의 요일을 랜덤으로 추출 
let rnd = Int.random(in: 0...Weekday.allCases.count)

// 좀 더 정확한 방법
Weekday.allCases.randomElement()

// .allCases가 배열을 리턴하므로 반복문에서 사용가능
for day in Weekday.allCases {
    print(day)
}

unknown default

선언한 열거형에 추가할 수 있다고 하여 Nonfrozen 열거형이라고 부릅니다. 그리고 해당 케이스를 전부 처리하지 않는 경우를 판단할 수 있게하는 @unknown default에 대해 알아보겠습니다.

enum ServiceType {
    case onlineCourse
    case offlineCamp
}
let selectedType = ServiceType.onlineCourse

switch selectedType {
    case .onlineCourse:
        print("~~")
    case .offlineCamp:
        print("hihi")
    // 케이스 전부를 처리하고 있지 않음을 알려주는 기능, 경고로 알려준다.
    @unknown default:
        break
}
profile
hi there 👋

0개의 댓글