文字列を指定した長さずつ分割する

Tuesday, March 22, 2016

夜中にAppleの新製品とかiOSとかSwiftとか色々発表されたみたいですね!
個人的にはSwift2.2正式版がでたのが嬉しいです。ライブラリとかちょこちょこ対応させる作業が必要ですが…笑

を見て、自分なりに実装してみようということでチャレンジしてみました。 (この記事にコメントしたのと同じものです。)


実装してみる

こんな感じで実装してみます。(swift2.1、多分2.2でも大丈夫…?)

public extension String {
    public func split(length: Int) -> [String] {
        let array = self.characters.map { String($0) }  // ★1
        let limit = self.characters.count // ★2
        return 0
            .stride(to: limit, by: length) // ★3
            .map({
                array[$0..<$0.advancedBy(length, limit: limit)].joinWithSeparator("") // ★4
            })
    }
}


実行してみる

let str = "Hello, world!"
_ = str.split(2)  //  ["He", "ll", "o,", " w", "or", "ld", "!"]
_ = str.split(3)  // ["Hel", "lo,", " wo", "rld", "!"]
_ = str.split(20) // ["Hello, world!"]


解説

上記のコードで を付けた箇所を順に見ていきます。

★1

まずは、Stringの文字列を、1文字ずつ区切った配列にします。

★2

文字数を取得します。後述の分割処理時に用います。

★3

stride関数を使って、指定した分割数で分割する場合の、各文字のstartIndexを作成します。
コードに改行が入っていて分かりづらいのですが、しっかりつなげて書くと、

0.stride(to: limit, by: length)

となっています。
このstride関数は、Strideableという protocol で定義されていて、
レシーバーの値から、to: に指定した値を超えない範囲で、by: で指定した値を足していった配列を作って返してくれるものです。
そして、StrideableIntFloat に最初から適応されています。 例えば、

10.stride(to: 23, by: 3)

としたら、得られる配列は、
[10, 13, 16, 19, 22]
となります。

★4

★3のstrideの処理で受けたstartIndexを元に文字列を分割して返します。
単純に、array[$0..<$0+length]としてしまうと、ぴったり分割できない場合にエラーになってしまうので、
$0.advancedBy(length, limit: limit)として、 limit を超えないendIndexを作って使います。
ただ、これで得られる値は[String]なので、joinWithSeparator("")をしてから返します。

おまけ: strideを使わないでfilterを使う場合

public extension String {
    public func split(length: Int) -> [String] {
        let array = self.characters.map { String($0) }
        let limit = self.characters.count
        return (0..<limit)
            .filter { $0 % length == 0}
            .map({
                array[$0..<$0.advancedBy(length, limit: limit)].joinWithSeparator("")
            })
    }
}

こんな感じですかね。見慣れないとはいえ、strideの方が短く書けるので個人的には気に入っています。


(追記)
もしかしたらSwift3.0でstride関数周りが違う名前に置き換わったりして、Swift2.1,2.2で使えているstrideの書き方だと将来的にエラーになるかもしれないので、時期がきたらそちらも踏まえて更新します。

techSwiftTips

[Swift] AnyGeneratorの今後

APIKitで定義したRequestからNSURLRequestを生成して使う