varでfor文を回してやる方法
例えばURLのGETパラメータをDictionaryにしたいってときに、var
でDictionary
を宣言してfor文の中で追加してくってのが最初思いつきます。
let queries = ["id=3", "token=abc", "tag=5"]
var params = [String: String]()
for q in queries {
let v = q.componentsSeparatedByString("=")
let (key, value) = (v[0], v[1])
params[key] = value
}
println(params)
// => ["id": "3", "token": "abc", "tag": "5"]
これだとparams
に新たに値を追加できちゃうのと、せっかくSwiftなのでmap
とかfilter
の高階関数使ってうまいことできないかなと思ったので考えてみました。
reduce
を使う
自分でmap
で色々やってみましたができなくてScalaが得意な同僚に聞いたところ、そういう場合はreduce
でできるということなのでやってみました。
以下のような感じです。
let queries = ["id=3", "token=abc", "tag=5"]
let params = queries.reduce([String: String]()) { (var dict, q) in
let v = q.componentsSeparatedByString("=")
let (key, value) = (v[0], v[1])
dict[key] = value
return dict
}
println(params)
// => ["id": "3", "token": "abc", "tag": "5"]
dict
のところをvar
で宣言してるもポイントです。最初は何もつけてなかったんですが、value
を突っ込めないなと思って試しにvar
をつけたらいけました。var
使っちゃってんじゃんというツッコミ入りそうですが閉じてるのでいいかなと・・
reduce
ではどんなことが起きてるのか
reduceの宣言を見てみます。
func reduce<U>(initial: U, combine: @noescape (U, T) -> U) -> U
今回の場合で言えば、U
は[String: String]
のDictionary型、T
はString
型となります。なので、当てはめてみて各ループでどのようなものが返ってきているかをまとめると、以下のようになります。各ループで返却された値が次のcombineのUになるっていうことですね。
# | combine: ([String: String], | String) | ->: [String: String] |
---|---|---|---|
1 |
initial で渡した[]
|
"id=3" | ["id": "3"] |
2 | ["id": "3"] | "token=abc" | ["id": "3", "token": "abc"] |
3 | ["id": "3", "token": "abc"] | "tag=5" | ["id": "3", "token": "abc", "tag": "5"] |
まとめ
可読性を考えたらどっちがいいかは微妙なところです。ただ、reduce
の動きがイマイチよくわかってなかったので理解が進んでよかったです。表にしてみたら分かった気がしました。reduce
のサンプルってたいてい配列の要素を全部足し算するとかしか出てこなくてあんまり使いどころのイメージ湧かないなと思ってましたが、再帰的な処理が出てきたときはreduce
を思い浮かべるといいようです。