[iOS 사전캠프] Array, Dictionary, Set 파고들기

DoyleHWorks·2024년 10월 11일
0
// 자주 사용하는 메서드
// Array 
var numbers: [Int] = [1, 2, 3]
let count: Int = numbers.count      // 배열 갯수 확인 : 3
let isEmpty: Bool = numbers.isEmpty // 배열 비었는지 확인 : false
numbers[0]        // 1
numbers[0...1]    // [1, 2]
numbers.first     // Optional(1)
numbers.last      // Optional(3)
numbers.append(4)                            // [1, 2, 3, 4]
numbers.append(contentsOf: [5, 6, 7])        // [1, 2, 3, 4, 5, 6, 7]
numbers.insert(0, at: 0)                      // [0, 1, 2, 3, 4, 5, 6, 7]
numbers.insert(contentsOf: [10, 100], at: 2)  // [0, 1, 10, 100, 2, 3, 4, 5, 6, 7]
numbers.remove(at: 2)             // [0, 1, 100, 2, 3, 4, 5, 6, 7]
numbers.removeFirst()             // [1, 100, 2, 3, 4, 5, 6, 7]
numbers.removeLast()              // [1, 100, 2, 3, 4, 5, 6]
numbers.popLast()                 // [1, 100, 2, 3, 4, 5]
numbers.removeSubrange(1...3)     // [1, 4, 5]
numbers.removeAll()               // [] 

// Array - 비교
var array1 = [1, 2, 3]
var array2 = [1, 2, 3]
var array3 = [1, 2, 3, 4, 5]
array1 == array2                    //true
array1.elementsEqual(array3)        //false

// Array - 정렬
let unsorted = [1, 5, 3, 8, 6, 10, 14]
unsorted.sort()                                         // unsorted 배열을 오름차순 정렬 [1, 3, 5, 6, 8, 10, 14]
unsorted.sort(by: >)                                    // unsorted 배열을 내림차순 정렬 [14, 10, 8, 6, 5, 3, 1]
let sortedArray = unsorted.sorted()                     // unsorted 배열을 오름차순 정렬하여 반환 [1, 3, 5, 6, 8, 10, 14]
let sortedArray2 = unsorted.sorted(by: >)               // unsorted 배열을 내림차순 정렬하여 반환 [14, 10, 8, 6, 5, 3, 1]

// Dictionary
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
airports.keys // ["YYZ", "DUB"]
airports.values // ["Toronto Pearson", "Dublin"]
airports.keys.sorted() // ["DUB", "YYZ"]
airports.values.sorted() // ["Dublin", "Toronto Pearson"]
airports["APL"] = "Apple International" // airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin", "APL": "Apple International"]
airports["APL"] = nil // key에 매칭된 value 값 초기화
print(airports.count) // airports.count는 2
print(airports.keys) // 딕셔너리 airports에 있는 모든 key들. 딕셔너리 airports에 있는 모든 key들
let newYyz = airports.updateValue("Hello YYZ", forKey: "YYZ") // 해당 key가 있다면 value를 덮어쓰고, 덮어쓰기 전 기존값울 반환
print(newYyz) // 출력값: Optional("Toronto Pearson")
print(airports["YYZ"]) // 출력값: Optional("Hello YYZ")
let newApl = airports.updateValue("Hello APL", forKey: "APL") // 해당 key가 없다면 그 key에 해당하는 value에 값을 추가하고 nil을 반환
print(newApl) // 출력값: nil
print(airports["APL"]) // 출력값: Optional("Hello APL")

// Set
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
favoriteGenres.isEmpty // false
favoriteGenres.count // 3
favoriteGenres.contains("Rock") // true
favoriteGenres.randomElement() // Hip hop (다른 element가 나올 수 있음)
favoriteGenres.remove("Rock") // "Rock" -> 삭제된 요소를 리턴
favoriteGenres // ["Classical", "Hip hop"]
favoriteGenres.remove(5) // nil -> 존재하지 않는 요소를 삭제했을 때 에러는 발생하지 않고 nil 리턴
favoriteGenres.removeAll() // []

let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]

// Set - 합집합
oddDigits.union(evenDigits).sorted() // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
// Set - 교집합
oddDigits.intersection(evenDigits).sorted() // []
// Set - 차집합
oddDigits.subtracting(singleDigitPrimeNumbers).sorted() // [1, 9]
// Set - 대칭 차집합
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted() // [1, 2, 9]

Step 3의 'Lv1 - 2. 성적 관리 시스템 제작하기' 과제에서 제공해준 예시이다.
오늘은 이 예시를 가지고 Array, Dictionary, Set 에 대해서 공부해보았다.

궁금해서 검색한 것들:

  • Array 메소드 - .removeLast.popLast의 차이

    • removeLast(): 배열의 마지막 요소를 제거하고, 그 요소를 반환한다. 배열이 비어있으면 오류(runtime error)를 발생시킨다.
    • popLast(): 배열의 마지막 요소를 제거하고, 그 요소를 옵셔널(Optional)로 반환한다. 배열이 비어 있을 경우, nil을 반환하고 오류는 발생하지 않는다.
  • Array 비교 - == 연산자와 elementsEqual() 메소드 이용의 차이

    • == 연산자는 배열의 모든 요소뿐만 아니라, 배열의 길이도 비교한다.
      • 동작 방식: 두 배열의 길이가 같고, 각 위치의 요소들이 모두 동일할 때 true를 반환한다.
      • 결과: 배열의 순서와 길이 모두 중요하게 고려된다.
    • elementsEqual() 메소드는 배열의 각 요소의 순서대로 비교한다.
      • 동작 방식: 기본적으로 두 배열의 각 요소를 앞에서부터 차례대로 비교하여 동일한지 확인한다. 길이가 달라도 비교를 시도하지만, 길이가 다른 경우 결국 false가 된다. 배열의 길이는 직접적으로 비교하지 않고, 요소의 순서와 값에 따라 비교한다.
      • 결과: 배열의 순서는 중요하지만, 길이는 중요하지 않다. 하지만, 길이가 다른 경우 비교가 끝까지 진행되지 않으므로 결국 false가 반환된다.
    • Array를 비교할 때 성능과 최적화 측면에서는 == 연산자가 더 효율적일 수 있다.
      • ==는 두 배열의 길이가 같지 않을 때 비교를 시작하기 전 바로 false를 반환한다.
      • 대신에 elemtnsEqual() 메소드는 Array뿐만 아니라 String (Chracter의 시퀀스), Range, Dictionary 등 다양한 시퀀스를 비교할 수 있다.
    • elementsEqual() 메소드는 순차적으로 비교하기 때문에 시퀀스의 순서가 중요하다.
      • Set는 그래서 해당 메소드를 사용할 수 없고 == 연산자를 사용해 비교해야 한다.
      • Dictionary 또한 순서가 없는 컬렉션이지만, Swift 5.0 부터는 삽입된 순서가 유지된다. 키-값 쌍이 삽입된 순서대로 시퀀스처럼 비교하게 되나, 본래 Dictionary는 순서가 없는 컬렉션으로 설계되었기에 순서에 의존하는 비교 방식은 안전하지 않다.
  • let으로 선언한 배열의 특징

    • 배열 자체의 변경 불가: let으로 선언된 배열은 배열 자체를 새로운 배열로 할당하거나, 배열의 참조를 변경할 수 없다.

      • 여기서 '참조를 변경'한다는 것은 변수나 상수가 가리키고 있는 메모리 주소 또는 데이터를 다른 것으로 바꾼다는 뜻이다. var로 선언한 배열과는 다르게, let으로 선언한 배열은 한 번 설정된 참조, 즉 메모리의 위치를 변경할 수 없다.
    • 배열 내부의 요소는 수정 가능: 배열이 가변적인 구조이기 때문에, 배열의 요소를 추가하거나 제거할 수 있다. Swift에서 배열은 값 타입이지만, 내부적으로 let 배열의 내용물(요소)은 변할 수 있는 가변적인 특성을 유지한다.

    • 배열 내부의 요소 수정 불가:

      let numbers = [1, 2, 3, 4]
      
      // 배열의 요소 추가 (불가능)
      // numbers.append(5) // 컴파일 에러: 'numbers'는 let 상수이므로 수정할 수 없습니다.
      
      // 배열의 요소 제거 (불가능)
      // numbers.remove(at: 1) // 컴파일 에러: 'numbers'는 let 상수이므로 수정할 수 없습니다.
      
      // 배열의 요소 수정 (불가능)
      // numbers[0] = 10 // 컴파일 에러: 'numbers'는 let 상수이므로 수정할 수 없습니다.
  • Set을 선언할 때 데이터 타입을 지정하지 않는 경우

    • Swift는 데이터 타입을 명시하지 않을 때, 데이터의 타입 추론을 통해 결정한다.
    • 따라서 타입이 명확할 때는 불필요한 타입 선언을 피할 수 있다.
    • 이는 코드의 가독성을 높이고 간결하게 만든다.
    • 반면에, 타입을 항상 명시적으로 선언하면 코드의 정확성에 기여할 수 있다.
  • Set 안의 데이터 타입이 추론되는 여러가지 방식

    • 초기화할 때의 타입 추론

      let favoriteGenres = Set(["Rock", "Classical", "Hip hop"])
      // 타입 추론에 의해 favoriteGenres는 Set<String>으로 결정됨
    • 타입이 명확할 때

      let oddDigits: Set = [1, 3, 5, 7, 9]
      // oddDigits는 Set<Int>로 추론됨
    • 타입을 사용하는 함수 또는 메서드

      let favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
      if favoriteGenres.contains("Rock") {
        print("We have Rock!")
      }
    • 타입을 제약하는 제네릭 메서드

      let evenDigits: Set = [0, 2, 4, 6, 8]
      let primes: Set = [2, 3, 5, 7]
      
      let commonElements = evenDigits.intersection(primes)
      // commonElements는 Set<Int>로 추론됨
      

      (제네릭 메서드가 뭔지는 일단 생략..)

  • 선언과 초기화의 차이

    • 순서: 변수는 반드시 선언 후 초기화되어야 한다. 초기화는 선언 이후에 이루어지는 것이 일반적이다.
    • 값: 선언만 하면 변수는 메모리에 존재하지만, 초기화를 하지 않으면 그 변수의 값을 사용할 수 없다. 초기화는 변수를 사용하기 전에 반드시 필요한 단계이다.
    • Set 선언
      var favoriteGenres: Set<String> // String 타입의 Set을 선언함. 현재는 빈 집합.
    • Set 초기화
      favoriteGenres = ["Rock", "Classical", "Hip hop"] // 값을 할당하여 초기화
    • Set의 선언과 초기화를 동시에 수행
      var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
  • Set을 선언 및 초기화하는 방식

    • 빈 Set 선언 및 초기화
      1. Set() 메서드 사용
        var emptySet1: Set<Int> = Set() // 빈 Int 집합
      2. [] 배열 구문 사용
        var emptySet2: Set<String> = [] // 빈 String 집합
        당연한 얘기지만, []를 사용해 Set를 초기화가 가능한 것은 앞의 'Set = ' 의 맥락이 있기에 가능한 것.
        []는 배열 등에서도 사용되기에 Set() 메서드를 사용하는 것과는 맥락적 차이가 있음.
        Set로 선언된 후 값이 초기화된다는 걸 인식하면 크게 헷갈릴 일은 없을 것 같다.
    • 초기값을 가진 Set 선언 및 초기화
      1. 리터럴 사용
        var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"] // 문자열 집합 초기화
      2. Set 초기화 메서드 사용
        var favoriteGenresUsingMethod: Set<String> = Set(["Rock", "Classical", "Hip hop"]) // 문자열 집합 초기화

(문제 풀이는 이 글에서 계속)

profile
Reciprocity lies in knowing enough

0개의 댓글