아요 개발 일기

[Swift] Collection Types (컬렉션 타입) 본문

Swift/The Swift Programming language

[Swift] Collection Types (컬렉션 타입)

소진이 2022. 12. 30. 11:50

안녕하세요 ~~~ :D 

오늘은 Collection Type 을 공부해보도록 하겠습니다!ㅎㅎ

이 글은 Swift 공식문서 The Swift 5.5 Language Guide - Collection Type 을 참고하여 작성하였습니다.

 

Swift는 값의 컬렉션을 저장하는 3개의 기본 컬렉션 타입 array(배열), set(집합), dictionary(딕셔너리)를 제공합니다.

 

  • Arrays(배열) - 정렬된 값의 컬렉션
  • Sets(함수) -  순서가 정렬되지 않은 고유한 값의 컬렉션
  • Dictionary(딕셔너리)- 순서가 정렬되지 않은 key-value(키-값)의 컬렉션

 

 

Swift에서의 array(배열), set(함수), dictionaries(딕셔너리)는 항상 저장할 수 있는 값과 키의 값이 명확합니다.

이는 실수로 컬렉션에 잘못된 타입의 값을 넣을 수 없다는 것을 의미합니다.

또한 컬렉션으로부터 가져오는 값의 타입에 대해서 확신할 수 있다는 것을 의미합니다.

 

주의
Swift의 array, set, dictionary 타입들은 generic collections(제네릭 컬렉션)으로 구현되어 있습니다. 
제네릭 타입과 컬렉션에 대한 자세한 정보는 Generices을 보세요.

Mutability of Collections ( 컬렉션의 변경가능성 )


array, set, dictionary을 

  • 변수(var)에 할당하는 경우 - 변경 가능 
  • 상수(let)에 할당하는 경우 - 변경 불가능

 

주의
값을 바꿀필요가 없는 컬렉션은 상수(let)으로 할당하는것이 좋은 습관입니다.
그렇게하는것이 코드를 이해하는 것이 더 쉬워지고 Swift 컴파일러가 컬렉션의 성능을 최적화할 수 있습니다.

Arrays (배열)


배열은 정렬된 목록에 같은 타입의 값을 저장합니다.

같은 값은 배열내의 다른 위치에서 여러번 나타날 수 있습니다.

 

주의
Swift의 Array 타입은 Foundation의 NSArray 클래스와 연결되어있습니다.
Foundation과 Cocoa로 Array를 사용하는 것에 대한 자세한 정보는 Bridging Between Array and NSArray  를 보세요.

Array Type Shortand Syntax (배열 타입의 축약형 문법)

 

Swift의 배열 타입은 Array<Element>로 작성하며, Element는 배열의 타입을 나타냅니다.

또한, [Element]의 축약 형태로도 사용할 수 있습니다. 

두 형식이 기능적으로 동일하지만, 주로 축약 형태를 선호합니다.

 


Creating an Empty Array (빈 배열의 생성)

 

아래와 같이 초기 문법을 사용해서 특정 타입의 비어있는 배열을 만들 수 있습니다.

var someInts = [Int]()
print("someInts is of type [Int] with \(someInt.count) items.")
// Prints "someInts is of type [Int] with 0 items.

 

배열의 타입 정보를 이미 제공하는 경우, [](빈 대괄호 한쌍)으로 작성하면 빈 배열 리터럴 즉 비어있는 배열을 만들 수 있습니다.

someInts.append(3)
// someInts now contains 1 value of type Int
someInts = []
// someInts is now an empty array, but is still of type [Int]

Creating an Array with Default Value (기본 값으로 빈 배열 생성)

 

 

타입의 기본 값을 나타내는 repeating 메소드와 반복된 횟수 값을 count 메소드를 이용해 기본 값으로 빈 배열을 생성할 수 있습니다.

var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]

Creating an Array by Adding Two Arrays Together (두개의 배열을 함께 추가하여 배열 생성)

 

서로 호환되는 타입의 기본 두개의 배열을 더하기 연산자(+)를 사용해서 새로운 배열을 만들 수 있습니다.

새로운 배열의 타입은 추가된 두 배열의 타입으로 추론됩니다.

 

var anotherThreeDouble = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles is type [Double], and equals [2.5, 2.5, 2.5]

var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles is inferred as [Double], and equals [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]

Creating an Array with an Array Literal (리터럴을 이용한 배열의 생성)

하나 이상의 값으로 배열을 작성하는 축약법입니다.

배열 리터럴은 값의 목록으로 저장되고 콤마로 구분되며, 대괄호 한쌍으로 감싸져 있습니다.

 

[value 1, value 2, value 3]

 

아래 예제는 String 값을 저장하는 shoppingList 배열을 만듭니다.

두 가지 형식이 있는데 첫번째는 타입을 선언해준 형식, 두번째는 타입 추론을 사용한 축약 형식입니다. 

var shoppingList: [String] = ["Egg", "Milk"]
// shoppingList has been initialized with two initial items

var shoppingList = ["Eggs", "Milk"] // 타입 추론 사용

 


Accessing and Modifying an Array (배열의 접근 및 변환)

메소드와 프로퍼티, 또는 서브스크립트 문법을 사용해서 배열을 사용하고 수정합니다.

 

 

배열의 원소 개수 확인

읽기 전용(read-only)인 count 프로퍼티를 사용합니다.

print("The shopping list contains \(shoppingList.count) items.")
// Prints "The shopping list contains 2 items."

 

배열이 비어있는지 확인

count 프로퍼티가 0인지 확인하기 위해서, Boolean isEmpty 프로퍼티를 사용합니다. 

if shoppingList.isEmpty {
    print("The shopping list is empty.")
} else {
    print("The shopping list is not empty.")
}
// The shopping list is not empty.

 

배열 원소 추가

append(_:) 메소드를 호출해서 배열의 끝 부분에 새로운 항목을 추가할 수 있습니다.

shoppingList.append("Four")
// shoppingList.count = 3

또는, 더하기 할당 연산자 ( += )를 사용하여 추가할수도 있습니다. 

shoppingList += ["Baking Powder"]
// shoppingList.count = 4
shoppingList += [Chocolate Spread", "Cheese", "Butter"]
// shoppingList.count = 7

 

배열의 특정 위치의 원소 접근

서브스크립트 문법(subscript syntax)을 사용해서 배열로부터 값을 가져오며, [] 안에 원하는 값의 인덱스를 전달합니다.

var firstItem = shoppingList[0]
// firstItem : "Eggs"

 

shoppingList[0] = "Six eggs" // 값 변경 eggs -> Six eggs

shoppingList[4..6] = ["Bananas", "Apples"]
// shoppingList now contains 6 items
// 4, 5, 6번째 인덱스 아이템을 Banana, Apples로 변환. 즉, 아이템 3개가 2개로 줄었다.

 

특정 위치에 원소 추가/ 삭제/ 접근

 

배열의 지정된 인덱스 위치에 항목을 삽입하기위해서 insert(_:at:)메소드를 사용합니다.

shoppingList.insert("Maple Syrup", at:0)
// shoppingList now contains 7 items
// "Maple Syrup" is now the fitst item in the list

 

배열의 특정 인덱스 항목을 제거하고, 제거된 항목을 반환하기위해서  remove(at:) 메소드를 사용합니다.

let mapleSyrup = shoppingList.remove(at: 0)
// the item that was at index 0 has just been removed 
// shoppingList now contains 6 items, and no Maple Syrup 
// the mapleSyrup constant is now equal to the removed "Maple Syrup" string


firstItem = shoppingList[0]
// firstItem : "Six eggs"

 

배열의 마지막 항목을 제거하길 원하는 경우에 count프로퍼티를 조회할 필요가 없는 removeLast()메소드를 사용합니다.

let apples = shoppingList.removeLast()
// the last item in the array has just been removed
// shoppingList now contains 5 items, and no apples
// the apples constant is now equal to the removed "Apples" string

 

Iterating Over an Array ( 배열 순회하기)

 

for-in 반복문으로 배열에 있는 모든 값을 순회할 수 있습니다.

for item in shoppingList {
    print(item)
}
// Six eggs
// Milk
// Flour
// Baking Powder
// Bananas

 

배열의 값뿐만아니라 인덱스도 필요한 경우, enumerated() 메소드를 사용합니다.

enumerated() 메소드는 각 항목에 대해 정수와 값으로 구성된 튜플을 반환합니다. 정수는 0에서 시작하고 각 항목에 대해 하나씩 올라갑니다. 반복의 일부분으로 임시적으로 상수나 변수로 튜플을 분해할 수 있습니다.

for (index, value) in shoppingList.enumerated() {
    print("Item \(index + 1): \(value)")
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas

for-in 반복문에 대한 자세한 사항은 블로그에있는 Control Flow 혹은 Language guide 문서를 참고하세요.

 

 


Sets(함수)

Set은 값의 순서가 없으며, 같은 타입의 고유한 값을 저장합니다.

항목들의 순서가 중요하지 않거나, 하나의 항목이 전체에서 한번만 필요한 경우에 Array대신 Sets를 사용할 수 있습니다.

 

NOTE
Swift의 Set 타입은 Foundation의 NSSet 클래스와 연결되어있습니다.
Foundation과 Cocoa로 Set을 사용하는 것에 대한 자세한 정보는 Bridging Between Set and NSSet 을 보세요.

Hash values for Set Types (Set 타입에 대한 해쉬 값)

하나의 타입을 Set에 저장하기 위해서는 반드시 hashable 이어야만 합니다. 

즉, 타입은 반드시 자체적으로 해쉬 값(hash value)을 계산하는 방법을 제공해야합니다.

a === b인 경우에, a.hasValue == b.hashValue인 것처럼, 하나의 해쉬 값은 모든 객체에 대해 같은지 비교하는 Int 값입니다.

Swift에서의 모든 기본타입인 String, Int, Double, Bool은 기본적으로 hashable이고, 

Set 값 또는 dictionary의 key 타입으로 사용될 수 있습니다.

연관된 값(Enumerations에 설명되어있음)이 없는 열거형의 case 값은 기본적으로 hashable입니다.

 

 

 NOTE
Swift 표준 라이브러리로부터 Hashable 프로토콜을 준수하도록 만들어
Set 값 타입 또는 Dictionary key 타입으로 자신의 사용자정의 타입을 사용할 수 있습니다.

필수 hash(into) 메소드 구현에 대한 자세한 내용은 포스팅 Hashable 혹은 공식문서를 참조하세요.
프로토콜 준수에 대한 정보는 포스팅 Protocaols(프로토콜) 혹은 공식문서를 참조하세요.

 

 


Set Type Syntax (Set 타입 문법)

Set 타입은 Set<Element>로 작성하며, Element는 Set에 저장가능한 타입입니다.

배열과는 다르게, 세트에는 축약 형식이 없습니다.

 

Creating and Initializing an Empty Set (빈 Set 생성 및 초기화)

 

초기화 문법을 사용해서 특정 타입의 비어 있는 Set를 만들 수 있습니다.

var letters = Set<Character>()
print("letters is of type Set<Character> with \(letters.count) items.")
//prints "letters is of type Set<Character> with 0 items."

이미 타입 정보가 제공되는 경우, 비어있는 배열로 비어있는 함수를 만들 수 있습니다.

letters.insert("a")
// letters now contains 1 value of type Character
letters = []
// letters is now an empty set, but is stil of type Set<Character>

 

Creating a Set with an Array Literal (배열 리터럴을 이용한 Set 생성)

 

하나 이상의 값을 함수 컬렉션을 만드는 축약 방법으로 배열 리터럴로 함수를 초기화 할 수 있습니다. 

 

var favoritGenres: Set<String> = ["Rock", "Classical", "Hip Hop"]
// favoriteGenres has been initialized with three initial items

Set타입은 배열 리터럴이 단독으로 추론될 수 없으며, Set 타입은 반드시 명시적으로 선언되어야 합니다.

하지만, 하나의 타입만을 포함하는 배열 리터럴로 초기화하는 경우에는 Set의 타입을 작성하지 않아도됩니다.

때문에 아래와 같이 축약 형식으로 작성될 수도 있습니다.

 

var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]

 

Accessing and Modifying a Set (Set의 접근과 변경)

 

세트 원소 개수 확인

읽기 전용(read-only)인 count 프로퍼티를 사용합니다.

print("I have \(favoriteGenres.count) favorite music genres")
// Prints "I have 3 favorite music genres."

 

세트 비어있는지 확인

count 프로퍼티가 0인지 확인하기 위해서, Boolean isEmpty 프로퍼티를 사용합니다. 

if favoriteGenres.isEmpty {
	print("As far as music goes, I'm not qicky.")
} else {
	print("I have particular music preferences.")
}
// Prints "I have particular music preferences."

 

세트 원소 추가

insert(_:) 메소드를 호출하면 함수에 새로운 항목을 추가할 수 있습니다.

favoriteGenres.insert("Jazz")
// favoriteGenres now contains 4 items

 

세트 원소 삭제

remove(_:) 메소드를 호출하여 함수에서 항목을 제거할 수 있습니다.

값을 반환하거나, 값이 없을때는 nil을 반환합니다.

또한,  removeAll() 메소드를 호출하면 함수의 모든 항목을 제거할 수도 있습니다.

if let removedGenre = favoriteGenres.remove("Rock") {
    print("\(removedGenre)? I'm over it.")
} else {
    print("I never much cared for that.")
}
// Prints "Rock? I'm over it."

 

세트 특정 항목을 포함하고있는지 확인

 

contains(_:) 메소드를 사용하면 함수가 특정항목을 포함하고 있는지 검사합니다.

if favoriteGenres.contains("Funk") {
    print("I get up on the good foot.")
} else {
    print("It's too funky in here.")
}
// Prints "It's too funky in here."

 

Interating Over a Set (Set 순회)

 

for genre in favoriteGenres {
    print("\(genre)")
}
// Classical
// Jazz
// Hip hop

 

for genre in favoriteGenres.sorted() {
    print("\(genre)")
}
// Classical
// Hip hop
// Jazz

Performing Set Operations (Set의 연산처리) 

 

Foundamental Set Operations (기본 Set 연산)

 

 

 

  • intersection(_:) method :  2개의 함수에서 공통 값만으로 새로운 함수 만듬
  • symmetricDifference(_:) method :  2개의 함수에서 공통 값을 제외한 각각의 세트에만 있는 값으로 새로운 함수 만듬
  • union(_:) method :  2개의 함수에있는 모든 값으로 새로운 함수를 만듬
  • subtracting(_:) method :  특정 함수에 있는 값으로 새로운 함수를 만듬

 

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

oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]

 

Set Membership and Equality (Set의 멤버십과 동등 비교)

 

b는 a의 하위세트(subset) <--> a는 b의 상위세트(superset)

b와 c와 분리(disjoint)

  • "is equal"(같음) 연산자 (==)는 2개의 함수가 모두 같은 값을 포함하고 있는지를 결정하기위해 사용
  • isSubset(of:) method : 함수의 모든 값들이 지정된 함수에 포함되는지 결정하기위해 사용
  • isSuperset(of:) method : 하나의 함수가 지정된 함수의 모든 값을 포함하는지를 결정하기위해 사용
  • isStrictSubset(of:) or isStrictSuperset(of:) method : 함수가 하위함수 혹은 상위함수인지 결정하기위해 사용
  • isDisjoint(with:) method : 2개의 함수가 공통적인 값이 없는지 결정하기 위해 사용 (공통값 X -> true 반환)

 

let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]

houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSuperset(of: houseAnimals)
// true
farmAnimals.isDisjoint(with: cityAnimals)
// true

Dictionaries (딕셔너리)

 

Dictionaries(딕셔너리)는 정의된 순서 없이 같은 타입의 key-value의 쌍으로 이루어진 컬렉션입니다.

각 값은 고유한 key로 연관되며, key는 딕셔너리의 값을 식별하는 동작을 합니다.

배열과는 다르게 딕셔너리는 값에 지정된 순서가 없습니다.

식별자를 기반으로 값을 찾을 필요가 없을때 딕셔너리를 사용하며, 실생활에서 사전으로 특정 단어에 대한 정의를 찾는 것과 거의 같은 방법입니다.

 

NOTE 
Swift의 Dictionary 타입은 Foundation의 NSDictionary 클래스와 연관되어있습니다.
Foundation과 Cocoa에서의 Dictionary 사용에 대한 자세한 정보는 Bridging Between Dictionary and NSDictionary 을 보세요

 

Dictionary Type Shorthand Syntax (딕셔너리 타입 축약 문법)

Swift 딕셔너리의 타입은 Dictionary<Key, Value>로 작성됩니다.

 

NOTE
dictionary(딕셔너리) Key 타입은 세트(set)의 값 타입처럼, 반드시 Hashable 프로토콜을 준수해야합니다.

또한 [key, value] 처럼 축약 형식으로 딕셔너리의 타입을 작성 할 수 있습니다.

주로 축약 형식이 선호되고 딕셔너리 타입과 관련된 가이드에서 전반적으로 사용됩니다.

 


Creating an Empty Dictionary ( 비어있는 딕셔너리 생성)

var namesOfIntegers: [Int: String] = [:]
// namesOfIntegers is an empty [Int: String] dictionary

 

타입 정보를 이미 제공한 경우, 빈 딕셔너리를 만들 수 있으며, [:]로 작성됩니다.

namesOfIntegers[16] = "sixteen"
// namesOfIntegers now contains 1 key-value pair
namesOfIntegers = [:]
// namesOfIntegers is once again an empty dictionary of type [Int: String]

 

Creating a Dictionary with a Dictionary Literal (리터럴을 이용한 딕셔너리 생성)

 

key-value 쌍으로 dictionary을 작성하는 축약 방법입니다.

key와 value를 : 로 구분하며 ,로 구분되며 []로 감싸져있습니다.

[key 1: value 1, key 2: value 2, key 3: value 3]
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]

var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"] 
// 위에서 선언한 타입과 일관된 타입이므로 따로 타입을 작성하지 않아도 됨

 

Accessing and Modifying a Dictionary (딕셔너리의 접근과 변경)

 

딕셔너리의 항목의 개수 확인

읽기 전용(read-only)인 count 프로퍼티를 사용합니다.

 

print("The airports dictionary contains \(airports.count) items.")
// Prints "The airports dictionary contains 2 items."

 

딕셔너리가 비어있는지 확인

count 프로퍼티가 0인지 확인하기 위해서, Boolean isEmpty 프로퍼티를 사용합니다. 

 

if airports.isEmpty {
    print("The airports dictionary is empty.")
} else {
    print("The airports dictionary isn't empty.")
}
// Prints "The airports dictionary isn't empty."

 

딕셔너리에 값 할당

// 값 할당
airports["LHR"] = "London" // the airports dictionary now contains 3 items

// 값 변경하기
airports["LHR"] = "London Heathrow" // the value for "LHR" has been changed to "London Heathrow"

 

딕셔너리 특정 키에 대한 값을 설정 및 업데이트

 

updateValue(_:forKey:) 메소드를 사용합니다.

키가 존재하지 않으면 값을 설정하고 존재하면 값을 업데이트 합니다.

하지만 updateValue(_:forKey:) 메소드는 업데이트를 수행하고나서 예전 값을 반환합니다. 이것으로 업데이트가 발생했는지 확인 할 수 있습니다.

 

또한 updateValue(_:forKey:) 메소드는 옵셔널 값을 반환합니다

예를 들어 String 타입의 딕셔너리라면, 메소드는 String? 타입의 값 또는 "옵셔널 String"을 반환합니다.

옵셔널 값을 업데이트하기 전에 기존 값 O -> 예전 값 반환 , 기존 값 X -> nil 반환 합니다.

 

if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
    print("The old value for DUB was \(oldValue).")
}
// Prints "The old value for DUB was Dublin."

 

딕셔너리의 특정 키에 대한 값 가져오기

딕셔너리는 존재하지 않은 키에 대해 요청하는 것도 가능합니다.

딕셔너리의 서브스크립트는 딕셔너리의 값 타입을 옵셔널 값으로 반환합니다.

딕셔너리가 요청한 키에 값이 존재하는 경우 -> 키에 대한 기존 값을 포함하는 옵셔널 값 반환

키의 값이 존재하지 않는 경우 -> nil 반환

if let airportName = airports["DUB"] {
    print("The name of the airport is \(airportName).")
} else {
    print("That airport isn't in the airports dictionary.")
}
// Prints "The name of the airport is Dublin Airport."

 

딕셔너리의 키-값쌍 제거하기

 

nil로 할당

airports["APL"] = "Apple International"
// "Apple International" isn't the real airport for APL, so delete it
airports["APL"] = nil
// APL has now been removed from the dictionary

removeValue(forKey:) 메소드를 사용하여 키-값 쌍을 제거합니다.

값이 존재하는 경우에는 키-값 쌍을 제거하고 제거된 값을 반환하며, 값이 없는 경우에는 nil을 반환합니다.

if let removedValue = airports.removeValue(forKey: "DUB") {
    print("The removed airport's name is \(removedValue).")
} else {
    print("The airports dictionary doesn't contain a value for DUB.")
}
// Prints "The removed airport's name is Dublin Airport."

 

Iterating Over a Dictionary (딕셔너리 순회하기)

 

for-in 반복문으로 딕셔너리에 있는 키-쌍을 순회할 수 있습니다.

딕셔너리에 있는 각 항목은 (key, value) 튜플로 반환되고, 튜플의 멤버로는 임시 상수나 변수로 분해할 수 있습니다.

for (airportCode, airportName) in airports {
    print("\(airportCode): \(airportName)")
}
// LHR: London Heathrow
// YYZ: Toronto Pearson

for-in 반복문에 대해 자세한 것은 포스팅 Control Flow(제어문)혹은 공식 문서를 참고하세요.

 

key-value 프로퍼티를 사용해서 딕셔너리의 키나 값의 컬렉션을 반복해서 가져 올 수 있습니다.

for airportCode in airports.keys {
    print("Airport code: \(airportCode)")
}
// Airport code: LHR
// Airport code: YYZ

for airportName in airports.values {
    print("Airport name: \(airportName)")
}
// Airport name: London Heathrow
// Airport name: Toronto Pearson

 

Array 인스턴스를 사용하는 API로 딕셔너리의 키나 값을 사용해야 하는 경우에, key나 value 프로퍼티로 새로운 배열을 초기화 합니다.

let airportCodes = [String](airports.keys)
// airportCodes is ["LHR", "YYZ"]

let airportNames = [String](airports.values)
// airportNames is ["London Heathrow", "Toronto Pearson"]

 

Swift의 Dictionary 타입은 정의된 순서가 없습니다. 

Dictionary에서 특정 순서로 키나 값을 반복하기 위해서는 key나 value 프로퍼티에서 sorted() 메소드를 사용합니다.


Collection Types 에 대해 공부해 보았습니다!!

뭔가 엄청 많고 복잡해보이는데 하나씩 뜯어보면 쉬운 것 같아요!!

다음은 Control Flow (제어문)에 대해서 공부해보겠습니다 ㅎㅎ 

다음 포스팅에서 봐요!! :D