[swift] String.CharacterViewが使いづらいと思う、そんなあなたにArray<Character>のおすすめ

# 書いたこと
* Array\<Character\>について
* String.CharacterViewとの比較
* Array\<Character\>をもっと便利に使うためには
* \[雑感\] String.CharacterViewが使いにくいと思う理由

# Array\<Character\>について
1. 生成方法について
1. n文字目の取得が楽ちんになります。
1. substringの取得が楽ちんになります。
1. Arrayのメソッドを通じて各種の文字列処理が出来ます。

## 生成方法について
String → String.characterView → Array\<Character\>で生成します。

```genCharacterArray.swift
//1. Stringを作る
let w = "Hello"
//2. String.CharacterViewにする
let charView = w.characters 
//3. Array<Character>へ変身!
let charArray = Array(charView)
```

## n文字目の取得が楽ちん!
subsript(position: Int)メソッドを通じて直接n文字目にアクセス出来ます。

```charArray_get_n_char.swift
let charArray = Array("Hello".characters)
// 先頭文字の取得
let c1 = charArray[0] // Character
let s1 = String(c1)   // Stringに変換

// 3文字目を取得
let c3 = charArray[2] // Character
let s3 = String(c3)   // Stringに変換
```

## 特定の範囲のsubstringの取得が楽ちん!
subscript(bounds: ClosedRange\<Int\>)メソッドを通じてサブ文字列を取得できます。

```charArray_get_substring.swift
let abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
// FGHIJ
let sub = abc[5..<10]   // ArraySlice<Array<Character>>
let fghij = String(sub) // Stringに変換
```

## Arrayのメソッドを通じて各種の文字列処理も簡単! Kantan!

### index(of:)メソッドで特定の文字列を取得
```charArray_index_of.swift
let charArray = Array("Hello World".characters)

// 最初のスペースから後ろの単語を取得します。 World
let secondPart = charArray.index(of: " ").flatMap { String(charArray[$0+1..<charArray.count])}
```

### split(separator:)メソッドで分割!
```charArray_split_separator.swift
let charArray = Array("Pen Pineapple Apple Pen".characters)

// ["Pen", "Pineapple", "Apple", "Pen"] 
let parts = charArray.split(separator: " ").map { String($0) }
```

### insert(contentsOf:, at:)メソッドで特定の位置に文字をインサート!
```charArray_insert_contentsOf_at.swift
var charArray = Array("Hi!".characters)
charArray.insert(contentsOf: " everyone".characters, at: charArray.count - 1)
// Hi everyone!
print(String(charArray))

```

# String.CharacterViewとの比較

## n文字目の取得

```characterView_get_n_char.swift
let word = "Hello"
// 1文字目を取得
let c1 = word.characters[word.startIndex] // Character
let s1 = String(c1) // Stringに変換する

// 3文字目を取得
let thirdIndex = word.characters.index(word.startIndex, offsetBy: 3)
let c3 = word.characters[thirdIndex] // Character 
let s3 = String(c3)
```
兎に角indexの生成が大変！:-1:

## 特定の範囲のsubstringの取得

```characterView_get_substring.swift
import Foundation // String.substring系のメソッドはFoundationの拡張のため
let abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
// FGHIJ
let startIndex = abc.characters.index(abc.startIndex, offsetBy: 5)
let endIndex = abc.characters.index(abc.startIndex, offsetBy: 10)
let sub = abc.characters[startIndex..<endIndex]
let fghij = String(sub) // Stringに変換
```
こちらも兎に角indexの生成が大変！:-1::-1:

# Array\<Character\>を便利に使う
1. typealiasにおける定義
2. string()メソッドの実装

## typealias CharacterArray
```
typealias CharacterArray = Array<Character>
```

### 使用例
```
// 型を定義しておくとコードリーディングがはかどります。
let charArray: CharacterArray = Array("Hello".characters)
```

## string()メソッドの実装
兎に角Stringへの変換がめんどくさいですよね。
そこでSequenceを拡張してStringに変換するstring()メソッドを実装しましょう。

```extension_sequence_for_string.swift
extension Sequence where Iterator.Element == Character {
    func string() -> String {
        return String(self)
    }
}
```

### 使用例
```
// 例１
let charArray = Array("Hello World".characters)
if let i = charArray.index(of: " ") {
   // World
   print(charArray[i+1..<charArray.count].string())
}

// 例2
var charArray2 = Array("Hi!".characters)
charArray2.insert(contentsOf: " everyone".characters, at: charArray2.count - 1)
// Hi everyone!
print(charArray2.string())

```

# 最後にString.CharacterViewが使いにくいと思う理由を考えてみる

結論から言うと、String.CharacterView.IndexがStrideableプロトコルを順守していないからだと思います。
従って以下の様なことが出来ません。

```
// 特定のCharacterView.Indexに+1して次のindexを生成できない。
let secondPartIndex = "Hello Swift".characters.indexOf(" ") + 1
```

CharacterViewの場合、特定のindexにアクセスするには以下のメソッドで生成する必要があります。

```String.CharacterView
CharacterView.index(_: CharacterView.Index, offsetBy: Int) -> String.CharacterView.Index
```

例: 2文字目のインデックスの生成

```
let word = "ABCD"
let secondIndex = word.characters.index(word.startIndex, offsetBy: 1)
// B
print(word.characters[secondIndex])
```

これがもしCharacterView.IndexがStrideableプロトコルを採用していると以下のように拡張することができます。

```
let word = "ABCD"
// B
print(word.characters[word.startIndex + 1])
```

もちろん上記のコードが範囲外にアクセスしてクラッシュする可能性があることも重々承知しておりますが、兎に角String.CharacterView.IndexがStrideableを採用してくれていれば、ある程度堅牢な作りに拡張することが可能になるはずです。