Swiftらしいコーディングを学ぶ 「CollectionType」

# はじめに
以前はobjcでiOS開発する機会が多かったのですが、最近SwiftでiOS開発をする機会が多いので、Swiftらしいコーディングをしていくために学んだことをまとめていきたいと思います。何か気になることや、もっとよく書けるみたいな意見があれば気軽にコメントお願いします。今回はCollectionTypeに準拠する配列や辞書についてまとめました。

# Collection Types

[CollectionType Protocol Reference](https://developer.apple.com/library/ios/documentation/Swift/Reference/Swift_CollectionType_Protocol/)を見てみると**Array**, **Set**, **Dictionary**は`Collection Types`に準拠していることがわかりますね。`Collection Types`に準拠したクラスは複数の明示された型のキーや値を格納することができ、それ以外の方を格納することは出来ません。つまり、キーや値の型を取得する際は想定された型を受け取ることが保証できます。

## 要素の重複と順序
型によって要素の順序や重複した要素を格納できるかが異なります。

| 型             | 要素の順序    | 要素の重複                         |
|:--------------:|:------------:|:---------------------------------:|
| Array          |要素を入れた順  |◯                                 |
| Set            |順不同         |×                                 |
| Dictionary     |順不同         |key **×**    value **◯**        |


## 変数定義

- [raywenderlich/swift-style-guide](https://github.com/raywenderlich/swift-style-guide)で推奨されている変数定義

```swift
// Array
var array: [Int] = []

//Dictionary
var dictionary: [Int: String] = [:]
```

- [Collection Types](https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html)で推奨されている変数定義

```swift
// Array
var array = [Int]()
var arrayWithValues = ["value1", "value2"]

// Set
var set = Set<Character>()
var setWithValues: Set = ["value1", "value2", "value3"]

//Dictionary
var dictionary = [Int: String]()
var dictionaryWithKeyValue = ["key1": "value1", "key2": "value2"]

//どうしても変数である必要がある場合以外はletで宣言する
let constArray = ["value1", "value2"]

```

- 以下の定義の仕方は非推奨

```swift

var array: [Int] = [Int]()
var array = Array<Int>()
var dictionary: [Int: String] = [Int: String]()
var dictionary = Dictionary<Int, String>()
```

## 基本的な使い方

以下の書き方は[Collection Types](https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html)を参考にしています。

### Array
```swift
var someInts = [Int]()
//[]
someInts.append(3)
//[3]
someInts += [2, 5]
//[3, 2, 5]
someInts = []
//[]

var threeDouble = [Double](count: 3, repeatedValue: 0.0)
// [0, 0, 0]
var anotherThreeDoubles = [Double](count: 3, repeatedValue: 2.3)
//[2.3, 2.3, 2.3]
var sixDouble = threeDouble + anotherThreeDoubles
//[0, 0, 0, 2.3, 2.3, 2.3]

//要素と一緒に初期化
var shoppingList = ["Eggs", "Milk"]
// ["Eggs", "Milk"]
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
//["Eggs", "Milk", "Chocolate Spread", "Cheese", "Butter"]
shoppingList.count
//5
shoppingList[0] = "two Eggs"
//["two Eggs", "Milk", "Chocolate Spread", "Cheese", "Butter"]
shoppingList[1...2] = ["Bananas", "Apples"]
//["two Eggs", "Bananas", "Apples", "Cheese", "Butter"]
shoppingList.insert("Maple Syrup", atIndex: 0)
//["Maple Syrup", "two Eggs", "Bananas", "Apples", "Cheese", "Butter"]

let mapleSyrup = shoppingList.removeAtIndex(0)
print(mapleSyrup) 
//"Maple Syrup"
print(shoppingList) 
//["two Eggs", "Bananas", "Apples", "Cheese", "Butter"]

let apples = shoppingList.removeLast()
print(apples)
//"Butter"
print(mapleSyrup)
//["two Eggs", "Bananas", "Apples", "Cheese"]

if shoppingList.isEmpty {
    print("empty")
} else {
    print("contain value")
}
//contain value

```

### Set
```swift
//空のSet
var letters = Set<Character>()
//[]
letters.insert("a")
//{"a"}
letters = []
//[]

//要素と一緒に初期化
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
//{"Rock", "Classical", "Hip hop"}
favoriteGenres.count
//3
favoriteGenres.insert("Jazz")
//{"Rock", "Classical", "Jazz", "Hip hop"}
//Jazzは最後に追加されてないことに注意

if let removedGenre = favoriteGenres.remove("Rock") {
    print("removed Rock")
}
//"removed Rock"

if favoriteGenres.contains("Hip hop") {
    print("contain Hip hop")
}
//"contain Hip hop"

if favoriteGenres.isEmpty {
    print("empty")
} else {
    print("contain value")
}
// "contain value"

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).sort()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersect(evenDigits).sort()
// []
oddDigits.subtract(singleDigitPrimeNumbers).sort()
// [1, 9]
oddDigits.exclusiveOr(singleDigitPrimeNumbers).sort()
// [1, 2, 9]
```

- `a.union(b)`      : 重複を除くaとbに含まれるすべての要素
- `a.intersect(b)`  : aとbので重複している要素
- `a.subtract(b)`   ：　aからaとbで重複している要素を除いた要素
- `a.exclusiveOr(b)`：　aとbで重複してない要素

### Dictionary

```swift
var namesOfIntegers = [Int: String]()
//[:]
namesOfIntegers[16] = "sixteen"
//[16: "sixteen"]
namesOfIntegers = [:]
//[:]

//要素と一緒に初期化
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
//["DUB": "Dublin", "YYZ": "Toronto Pearson"]
airports.count
//2

airports["LHR"] = "London"
//["DUB": "Dublin", "LHR": "London", "YYZ": "Toronto Pearson"]
airports["LHR"] = "London Heathrow"
//["DUB": "Dublin", "LHR": "London Heathrow", "YYZ": "Toronto Pearson"]

if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
    print(oldValue)
    //Dublin
}
print(airports)
//"["DUB": "Dublin Airport", "LHR": "London Heathrow", "YYZ": "Toronto Pearson"]

if let airportName = airports["DUB"] {
    print(airportName)
    //Dublin Airport
}
airports["APL"] = "Apple International"
//["APL": "Apple International", "YYZ": "Toronto Pearson", "DUB": "Dublin Airport", "LHR": "London Heathrow"]
airports["APL"] = nil
//["YYZ": "Toronto Pearson", "DUB": "Dublin Airport", "LHR": "London Heathrow"]
//APLのキーごと要素がなくなる

if let removedValue = airports.removeValueForKey("DUB") {
    print(removedValue)
    //Dublin Airport
    print(airports)
    //["YYZ": "Toronto Pearson", "LHR": "London Heathrow"]
}

if airports.isEmpty {
    print("empty")
} else {
    print("contain elements")
}
//contain elements

let airportCodes = [String](airports.keys)
//["YYZ", "LHR"]
let airportNames = [String](airports.values)
//["Toronto Pearson", "London Heathrow"]
```

## For-In Loops
要素が複数格納されている`Collection Types`に準拠する型はloop処理によって要素を参照することができます。

```swift
//Array
let array = ["Qiita", "Swift", "Objective-C", "iOS"]

for value in array {
    print(value)
}
//Qiita
//Swift
//Objective-C
//iOS

//enumerate()は配列のindexと値のタプルを返す
for (index, value) in array.enumerate() {
    print("index: \(index) value: \(value)")
}
//index: 0 value: Qiita
//index: 1 value: Swift
//index: 2 value: Objective-C
//index: 3 value: iOS

//Dictionary
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
    print("\(animalName)s have \(legCount) legs")
}
// ants have 6 legs
// cats have 4 legs
// spiders have 8 legs
```

## 高階関数
`Collection Types`に準拠する**Array**, **Set**や**Dictionary**などは`map`などの高階関数が利用できます。高階関数や`Closure`については[Swiftらしいコーディングを学ぶ 「コレクションに用いる高階関数とClosure」](http://qiita.com/shoheiyokoyama/items/ad884aeea2dc69baf422)の記事にまとめてあります。

# 参考
- [Collection Types](https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html)
- [CollectionType Protocol Reference](https://developer.apple.com/library/ios/documentation/Swift/Reference/Swift_CollectionType_Protocol/)
- [Swiftのコレクション型（Collection Types）](http://swiftrithm.com/type-collection/)
- [Control Flow](https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html#//apple_ref/doc/uid/TP40014097-CH9-ID121)
- [Swiftコーディング規約@Wantedly](http://qiita.com/susieyy/items/f71435cc962e70d81b37)
- [raywenderlich/swift-style-guide](https://github.com/raywenderlich/swift-style-guide)