【Swift】配列操作まとめ

#目次
[はじめに](#はじめに)
[部分置換](#部分置換)
[部分配列の型](#部分配列の型)
[配列のプロパティ](#配列のプロパティ)
[配列のメソッド](#配列のメソッド)
[シーケンス操作](#シーケンス操作)
[最後に](#最後に)

#はじめに
配列操作のまとめです。主に、部分配列、配列のプロパティ、メソッド、シーケンス操作についてまとめています。
SequenceとCollectionや基本的な配列のアクセス方法、その他配列以外の知識が必要なものは今回は説明していません。

#部分置換
```swift
var a = ["赤", "青", "緑", "白", "黒"]
//①
a[1...1] = ["橙", "茶", "紫"]
print(a) // ["赤", "橙", "茶", "紫", "緑", "白", "黒"]
//②
a[2...3] = ["黄"]
print(a) // ["赤", "橙", "黄", "緑", "白", "黒"]
//③
a[1...2] = []
print(a) // ["赤", "緑", "白", "黒"]
//④
a[2...] = ["銀"]
print(a) // ["赤", "緑", "銀"]
//⑤
a[...] = ["金"]
print(a) // ["金"]
```
注意
番号ごとに毎回配列の要素が変わっていることに注意してください。(前番号のコメントを参照)
① `"青"`を`"橙", "茶", "紫"`に変更しています。ただし、`a[1]`ではだめです。
② index番号が2から3である`"茶"`と`"紫"` を`"黄"`に変更しています。
③ index番号が1から2である`"橙"`と`"黄"` を消去しています。
④ index番号が2から最後の要素までの`"白"`と`"黒"`を`"銀"`に変更しています。
⑤ `...`で配列の全要素を指定し、`"金"`に変更しています。

#部分配列の型

```swift
let b = ["北", "東", "南", "西"]
let c = b[2...]
print(type(of: c)) // ArraySlice<String>
```
部分配列とは配列の添字に範囲を指定したものです。(a[4...5], a[2...], a[...1], a[...]など)
これらの型はArray型ではなく、ArraySlice型になります。
ArraySlice型は必ずしも0からインデックス番号が振られるわけではありません。

```swift
print(c.startIndex) // 2
print(c[2]) // 南
print(c[3]) // 西
```
このように、ArraySlice型は必ずしも0からインデックス番号が始まるとは限りません。
また、以下のようにすることでArraySlice型を0からインデックス番号が始まるArray型にすることができます。

```swift
let d = [String](c)
print(d) // ["南", "西"]
print(d.startIndex) // 0
```

#配列のプロパティ
|概要|宣言|説明|
|:---|:---|:---|
|要素数|count|要素数を整数値で返す|
|先頭要素|first|最初の要素を返す(なければnilを返す)|
|末尾要素|last|最後の要素を返す(なければnilを返す)|
|空配列か|isEmpty|空配列であればtrue,要素があればfalseを返す|

```swift
let e = [Character]("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
print(e) // "A"から"Z"までの一文字ずつの配列
print(e.count) // 26
print(e.first!) // A
print(e.last!) // Z
print(e.isEmpty) // false
```
#配列のメソッド
|概要|宣言|説明|
|:---|:---|:---|
|検索|firstIndex|引数の値が最初に現れる位置の添字を返す(なければnilを返す)|
|検索|lastIndex|引数の値が最後に現れる位置の添字を返す(なければnilを返す)|
|部分列|prefix|指定した個数分の先頭部分の部分配列を返す|
|部分列|suffix|指定した個数分の末尾部分の部分配列を返す|
|部分列|dropFirst|指定した個数分の先頭要素を取り除いた残りを部分配列として返す|
|部分列|dropLast|指定した個数分の末尾要素を取り除いた残りを部分配列として返す|
|追加|append|配列の末尾に値を付け加える|
|挿入|insert|指定した位置に値を挿入する|
|削除|remove|指定した位置の要素を削除する|
|削除|removeFirst|最初の要素を消去する|
|削除|removeLast|最後の要素を消去する|
|削除|removeAll|要素を全て消去し、配列の要素数を0にする|
|整列|sort|要素を昇順に並べ替える|
|整列|sorted|要素を昇順に並べ替えて新しい配列を返す|
|ランダム|randomElement|要素をランダムに選んで一つ返す|
|シャッフル|shuffle|要素をランダムに並び替える|
|シャッフル|shuffled|要素をランダムに並び替えた配列を返す|
|逆順|reverse|要素を元の配列と逆にする|
|逆順|reversed|要素を元の配列と逆にした配列を返す|

```swift
var f = ["A", "B", "B", "C", "C", "D", "E"]
print(f.firstIndex(of: "B")!) // 1
print(f.lastIndex(of: "C")!) // 4
print(f.prefix(3)) // ["A", "B", "B"]
print(f.suffix(5)) // ["B", "C", "C", "D", "E"]
print(f.dropFirst()) // ["B", "B", "C", "C", "D", "E"]
print(f.dropFirst(3)) // ["C", "C", "D", "E"]
print(f.dropLast()) // ["A", "B", "B", "C", "C", "D"]
print(f.dropLast(5)) // ["A", "B"]
f.append("G") // f -> ["A", "B", "B", "C", "C", "D", "E", "G"]
f.insert("H", at: 3) // -> ["A", "B", "B", "H", "C", "C", "D", "E", "G"]
f.remove(at: 5) // f -> ["A", "B", "B", "H", "C", "D", "E", "G"]
f.removeFirst() // f -> ["B", "B", "H", "C", "D", "E", "G"]
f.removeLast() // f -> ["B", "B", "H", "C", "D", "E"]
f.removeFirst(2) // f -> ["H", "C", "D", "E"]
f.removeLast(2) // f -> ["H", "C"]
f.removeAll() // f -> []
var g = [2, 4, 1, 6, 5, 3]
g.sort() // g -> [1, 2, 3, 4, 5, 6]
var h = ["a", "c", "b", "e", "d"]
let i = h.sorted()
print(h) // ["a", "c", "b", "e", "d"]
print(i) // ["a", "b", "c", "d", "e"]
var 宝くじ = ["100円", "300円", "1000円", "1万円", "100万円", "ハズレ"]
print(宝くじ.randomElement()!) // たとえば"ハズレ"
var j = [2, 4, 1, 6, 5, 3]
j.shuffle() // j -> たとえば[2, 5, 3, 1, 6, 4]
var k = ["a", "e", "c", "d", "b"]
let l = k.shuffled()
print(k) // ["a", "e", "c", "d", "b"]
print(l) // ["d", "a", "e", "c", "b"]
var m = ["S", "w", "i", "f", "t"]
m.reverse() // ["t", "f", "i", "w", "S"]
print(m)
let n = [1, 2, 3]
let o = n.reversed()
print(o) // ReversedCollection<Array<Int>>(_base: [1, 2, 3]) え？
```

注意するべきことのみ解説します。
`prefix`, `suffix`, `dropFirst`, `dropLast`はArraySlice型が返されます。
FirstやLastのついたメソッドに引数を指定するとたとえばLastであれば、最後から引数で指定したインデックス番号までを操作の対象とします。
`sort()`と`sorted()`の違いは`sort()`が配列そのものを変更するのに対し、`sorted()`は配列そのものは変更を加えず、新しく配列を作り、返します。`shuffle()`と`shuffled() `の違いも同様です。
`reversed()`は、、、見なかったことにしたい。。。`reversed()`は実際に要素が逆順になったArrayを生成せず、そのようにふるまうReversedCollection<Base>という型のインスタンスを返します。
参考記事を貼っておきます([reversed()の参考記事](https://rusutikaa.github.io/docs/developer.apple.com/documentation/swift/array/1690025-reversed.html))

#シーケンス操作
####filter
条件を満たすものだけを取り出し、新しい配列を作ることができる。
たとえば、100までの正の整数で13または43の倍数であるものを含む配列を作ってみます。

```swift
let p = [Int](1...100)
let q = p.filter {
    $0 % 13 == 0 || $0 % 43 == 0
}
print(q) // [13, 26, 39, 43, 52, 65, 78, 86, 91]
```
クロージャの詳しい解説はしませんが、`$0`に1から100まで順番に入っていきます。初めは1が入り、{}のなかの条件式`$0 % 13 == 0 || $0 % 43 == 0`はfalseになるので、1は作成される配列の中に入りません。次に2が入り、、、というふうに一つ一つの要素が条件を満たすか確認していき、条件を満たす(trueを返す)要素のみの配列を新しく作成し、定数qに格納しています。

####map
配列の各要素に指定した処理を適応させ、その結果からなる新しい配列を作成することができる。
たとえば、次のように配列rの全ての要素を2倍した配列を新たに作りたいとします。

```swift
let r = [10, 20, 30, 40]
let s = r.map {
    $0 * 2
}
print(s) // [20, 40, 60, 80]
```
`$0`に10から40まで順番に入っていき、条件式`$0 * 2`で全ての配列の要素を2倍しています。その配列を定数sに格納しています。
たとえば、次のようにfilterを合わせて使うこともできます。

```swift
let t = ["佐々木", "田中", "山本", "大和田"]
let u = t.map {
    $0 + "さん"
}.filter {
    $0.count >= 5
}
print(u) // ["佐々木さん", "大和田さん"]
```
まず、`map`で配列tの各要素に"さん"をつけています。`map`によって新たに作られた配列に、さらに`filter`で文字数が5以上になった場合の配列を新たに作っています。その新たに作られた配列を定数uに格納しています。

####mapValues

mapの返り値は辞書も集合も要素が格納された配列です。ただし、辞書については、含まれる要素の値に対して処理を適応し、その結果としてキーを組み合わせて新しい辞書を返すメソッドが用意されています。

```swift
let steps = [90, 80, 70, 60]
let evals = ["秀", "優", "良", "可", "不可"]
let v = ["太郎": 68, "二郎": 50, "三郎": 62, "四郎": 90]
let marks = v.mapValues { (point: Int) -> String in
    var index = 0
    for step in steps {
        if point >= step {
            break
        }
        index += 1
    }
    return evals[index]
}
print(marks) //たとえば["二郎": "不可", "四郎": "秀", "太郎": "可", "三郎": "可"]
```
ただし、辞書は配列のように順番通りに出力されないことに注意してください。今回の場合、`v`が辞書で、`mapValues`したものを定数`marks`に格納しているので、`print(marks)`すると順番が変わることがあります。

####compactMap
mapやmapValueはどちらも処理前と処理後の一対一に対応しています。しかし、目的によってはfilterのように不要な要素を取り除きたいということもあります。そのような時に用いられるシーケンスのメソッドがcompactMapです。

```swift
let people = [
    ["Name": "れおん", "Pet": "ハリネズミ", "BloodType": "O"],
    ["Name": "承太郎", "BloodType": "B"],
    ["Name": "花京院", "身長": "178cm"],
    ["Name": "ポルナレフ", "Pet": "イギー", "BloodType": "AB"]
]
let pets = people.compactMap {
    $0["Pet"]
}
print(pets) // ["ハリネズミ", "イギー"]
```
承太郎と花京院は`"Pet"`を持っていないので`compactMap`の条件式`$0["Pet"]`がnilになり、ハリネズミとイギーが入った配列が新たに作られています。

####flatMap
flatMapはすべての要素をシーケンスへと変換し、さらに、 それを1つのシーケンスに連結します。

```swift
let bookLists = [
    "Aさん": ["本1", "本2"],
    "Bさん": [],
    "Cさん": ["本3"]
]
let books = bookLists.flatMap {
    $0.value
}
print(books) // たとえば["本3", "本1", "本2"]
```


####reduce
シーケンスに含まれる要素を一つずつ全て使って処理を行い、その結果として一つの値を返します。
たとえば、数値の列の合計を求めるには次のようにします。

```swift
let w = [1, 2, 5, 1, 8, 2, 3]
let sum = w.reduce(0) {
    $0 + $1
}
print(sum) // 22
```
一周目は`$0`に第一引数にある初期値`0`が入り、`$1`には配列の初めの要素`1`が入ります。よって、`0 + 1`で`1`が得られる。
二周目は`$0`に先ほど一周目で得られた`1`が入り、`$1`には配列の二番目の要素`2`が入ります。よって、`1 + 2`で`3`が得られる。
三周目は`$0`に先ほど二周目で得られた`3`が入り、`$1`には配列の三番目の要素`5`が入ります。よって、`3 + 5`で`8`が得られる。
これを繰り返していくと、配列wの合計値が求められる。

####forEach
配列の各要素に順番に適用していく。
for-in文と基本的には同じだが、forEachはbreakなどが使えない。

```swift
let numberWords = ["one", "two", "three"]
numberWords.forEach {
    print($0)
}
//"one"
//"two"
//"three"
```

####enumerated
シーケンスから取り出した順の通し番号と要素からなるタプルを含むシーケンスを返す。

```swift
let names = ["名前A", "名前B", "名前C"]
for t in names.enumerated() {
    print(t)
}
//(offset: 0, element: "名前A")
//(offset: 1, element: "名前B")
//(offset: 2, element: "名前C")
```
タプルのラベルは無視しても良い。

####zip
二つのシーケンスを引数としてそれぞれの要素をタプルとする新しいシーケンスを返します。

```swift
for t in zip("アイウエオ", [1, 2, 3]) {
    print(t)
}
//("ア", 1)
//("イ", 2)
//("ウ", 3)
```

####repeatElement
引数で指定したインスタンスを指定した個数だけ含むシーケンスを作成して返します。

```swift
for t in zip(["佐々木", "田中", "佐藤"], repeatElement("Hello", count: 2)) {
    print(t)
}
//("佐々木", "Hello")
//("田中", "Hello")
```


####sequence
最初から全ての要素が決まっているのではなく、必要が生じてから次の要素を計算して返すという、遅延評価によるシーケンスのインスタンス生成して返します。

```swift
var seq = sequence(first: 10000000, next: { $0 / 10 })
for p in seq.prefix(10) {
    print(p)
}
//10000000
//1000000
//100000
//10000
//1000
//100
//10
//1
//0
//0
```

####lazy
遅延評価を行うとシーケンスの全要素を処理する必要がなくなったり、最初の結果をすばやく得たりできる場合があります。
lazyを利用して実行順序が変化する例を示します。

```swift
func f(_ n: Int) -> Bool {
    print(n, terminator: " ")
    return n & 1 == 0
}
(0...5).filter(f).forEach {
    print("[\($0)]", terminator: " ") // 0 1 2 3 4 5 [0] [2] [4]
}
(0...5).lazy.filter(f).forEach {
    print("[\($0)]", terminator: " ") // 0 [0] 1 2 [2] 3 4 [4] 5 
}
```

#最後に
初めての投稿ですが、なんとか書き終えれてよかったです。
Swift難しい。。。