Swift 4 でキャメルケースとスネークケースを変換する String 拡張

### 2017-12-16 追記
Swift 4 でも動くように書き直しました。いや〜、様変わりしましたね。

## 標準ライブラリにない……！！！
ウギャーーー！よく使いそうなのに！
ケースのポリシーが違うシステム間でデータをやりとりするとき（json でデータをやりとりするときとか）、変数名を自動で変換したかったのでしこしこ書いた。

追記: 今は Codable という便利な仕組みがあるので、json でやり取りしたいときのケース変換はそっちを使うほうが良いでしょう。


## コード
### スネークケース → キャメルケース
```swift
// Swift 4 版 (String の extension として実装)
extension String {
    // set `lower = false` in case you want "UpperCamel" output.
    func camelized(lower: Bool = true) -> String {
        guard self != "" else { return self }

        let words = lowercased().split(separator: "_").map({ String($0) })
        let firstWord: String = words.first ?? ""

        let camel: String = lower ? firstWord : String(firstWord.prefix(1).capitalized) + String(firstWord.suffix(from: index(after: startIndex)))
        return words.dropFirst().reduce(into: camel, { camel, word in
            camel.append(String(word.prefix(1).capitalized) + String(word.suffix(from: index(after: startIndex))))
        })
    }
}
```

```swift
// Swift 2 版
// set `lower = false` in case you want "UpperCamel" output.
func snakeIntoCamel (snake:String, lower:Bool=true) -> String {
    let pattern = "(\\w{0,1})_"
    var camel = snake.capitalizedString.stringByReplacingOccurrencesOfString(pattern, withString: "$1",
        options: NSStringCompareOptions.RegularExpressionSearch, range: nil)

    if lower {
        // make the first letter lower case
        let head = snake.substringToIndex(advance(snake.startIndex, 1))
        camel.replaceRange(camel.startIndex...camel.startIndex, with: head.lowercaseString)
    }

    return camel
}
```

### キャメルケース → スネークケース
```swift
// Swift 4 版 (String の extension として実装)
extension String {
    var snakenized: String {
        // Ensure the first letter is capital
        let head = String(prefix(1))
        let tail = String(suffix(count - 1))
        let upperCased = head.uppercased() + tail

        let input = upperCased

        // split input string into words with Capital letter
        let pattern = "[A-Z]+[a-z,\\d]*"
        guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else {
            return ""
        }

        var words:[String] = []

        regex.matches(in: input, options: [], range: NSRange.init(location: 0, length: count)).forEach { match in
            if let range = Range(match.range(at: 0), in: self) {
                words.append(String(self[range]).lowercased())
            }
        }

        return words.joined(separator: "_")
    }
}
```

```swift
// Swift 2 版
func camelIntoSnake (camel:String) -> String {
    // ensure the first letter is capital
    let head = camel.substringToIndex(advance(camel.startIndex, 1))
    var upperCased = camel
    upperCased.replaceRange(camel.startIndex...camel.startIndex, with: head.uppercaseString)

    var input = upperCased as NSString

    // split input string into words
    let pattern = "[A-Z]+[a-z,\\d]*"
    let regex = NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions.allZeros, error: nil)

    var words:[String] = []

    if let matches = regex?.matchesInString(input as String, options: NSMatchingOptions.allZeros, range: NSRange(location: 0, length: input.length)) {
        for match in matches {
            for var i = 0; i < match.numberOfRanges; ++i {
                // make every word in lower case
                words.append(input.substringWithRange(match.rangeAtIndex(i)).lowercaseString)
            }
        }
    }

    return join("_", words)
}
```

Swift、というか Foundation フレームワークでは正規表現がめっちゃめんどくさいですな。。。
目がぱちぱちするね！

## 参考
http://www.learnswiftonline.com/reference-guides/string-reference-guide-for-swift/
http://blog.mogmet.com/swift-regular-expression-replace/