Swift: map()でゴチャついた処理をreduce(into:)でスッキリ

こんつぁ、しょーです。

イテレーション、繰り返し処理の話をしよう。

reduce(into:)はちょっと理解あまりしていなく使用を避けていた節があり、map系でゴリゴリしていたらこれ使えるよってアドバイスもらって使い方がわかったので備忘録。

想定

Official

APIレスポンスを元にTableViewにて、セクションを切ってアイテムをリスト表示させたいケースがありました。

image.png

Header

SectionHeaderは別途使用していたのと、viewをいじいじしたCustomViewを使用したかったなどで、headerCustomCellを使用したかった。

Cell

普通にcustomCell

こんな感じのリストがレスポンスとして返ってくるとします。

これをこうしたい。

image.png

なんならアイテムが無ければこうもしたい。

image.png

TableView.CellForRowAtで操作しやすくするためにベースとなる配列を作成したい、というところ。

条件

  • ヘッダーの文字列 + それに対応したアイテムリスト を作成したい
  • アイテムの数に比例したセル数を作成する必要があるので、個数も対応したい
  • アイテムがない場合はヘッダーも要らないので、そのセクションは排除したい

TableViewの実装方法にもよりますが、割愛します。
これを実現させるために以下のような配列が必要。(1つの案として)

こうすることで、cellForAtRowではswitch sections[indexPath.section]を切って別々のカスタムセルを付与できるし、
配列なのでindexPath.row/sectionの数も合います。

では作ろうということで、以下を実装しました。

コード

前置き長くなりましたがreduce(into:)の本題。
最初はmap系駆使してやりました。

第一形態

  • kind & namesJP をセットで操作したいのでタプル型の配列を作る
  • アイテムがない場合はセクションを排除したいので、isEmpty判定でなければnilを返す
  • compactMapnil掃除
  • 一時的に作成した配列と、最終的に使用したい配列の型が異なるのでflatMapで欲しいものだけ取り出す

これでも動きますが、マップマップしてちょっと騒がしい。

ということで提案いただいたその1が以下。

第二形態

一時的な配列を作成しないで一気に作成。少し静かになりました。

しかし、これはあと一回進化を残しています。その意味が分か
ここでreduce(into:)を使用します。

第三形態

なんということでしょう、ほぼほぼ2行になりました。
凝縮していますが、まだリーダブルの範囲です。慣れればサクッとリーディングできそうです。

reduce(into:)の使い方

こういう時にreduce(into:)使えるかも?と閃くキッカケとして

  • 何かをベースに配列や辞書(ベースとは別の型)を作成したい時
  • nilの場合、該当する値を排除したい時(nilを許容しない場合)

flatMap / compactMapなどカリカリする時があれば閃ければいいです。
APIレスポンスを操作するときなどマッピングする機会はたくさんあると思うので使い所はありそうですし、スッキリしていいですね。

まとめ

マッピング系処理のネタが増えてよかった。
読みもそうだけど、実装できるか判断するまで慣れが必要そうね。

お疲れっす!