CakePHP3のCollectionInterface
えっ知らないの? プギャー
- コレクション
- CollectionInterface
- implemented by ResultSet
こういうやつです。
$productNames = collection($orderDetails) ->extract("productName") ->toList(); $totalPriceExTax = collection($orderDetails) ->map(function($orderDetail) { return $orderDetail->subtotalExTax(); }) ->reduce(function($accum, $price) { return $accum + $price; }, 0);
何が良いの? for文で良くね?
はい、動きゃあどっちでも良いです。
$productNames = []; foreach ($orderDetails as $orderDetail) { $productNames[] = $orderDetail->productName; } $totalPriceExTax = 0; foreach ($orderDetails as $orderDetail) { $totalPriceExTax += $orderDetail->subtotalExTax(); }
ただ、明確な違いがあるんです。
つまり、
- コレクションの変換操作を
- 関数型プログラミング的アプローチで変換の定義を宣言しているか
- 式
- 手続き型プログラミングのアプローチで処理を素朴に書き下しているか
- 文の集まり
- 関数型プログラミング的アプローチで変換の定義を宣言しているか
のようなパラダイムの違いがあるんです。
個人的には、くだらない些細な処理はいちいちfor文でループするより、前者のアプローチでやったほうがやっていることが明確でわかりやすい場合が多いと思う (ケースバイケースだけど) 。
別の観点として、for文なんか置いておくと最初はシンプルな記述でも、時を経て改修に改修を重ねられた結果、雑多な処理が付け加わっていって意味不明になるケース、ありがちですね。
underscore.js ??
もし今までに underscore.js を使用したことがあるなら、コレクションクラスに何を期待できるかの考え方を理解しているはずです。
コレクション
CakePHPのドキュメントにもサラッと書いてあるのですが、このインタフェースって遡るとJavaScriptの underscore.js という有名なライブラリに端を発しているんです。[0]
_.map([1, 2, 3], function(num){ return num * 3; }); => [3, 6, 9]
どのくらい有名かって?
そりゃもうあれだよね、会えなくても瞳を閉じればふと浮かぶあの人との甘美な思い出のごとく、他の言語を書いてても忘れらんねえよベイベーな中毒者が続出するレベル 。 (例: underscore.php, underscore.py, lodash(Underscore.js)まとめ, Swift, Objective-C, PHPもあるよ)
その後、underscore.jsは遅いだかサイズがデカイだかで lodash.js という似たようなライブラリが出たけど、モノとしてはあんまり変わりない (雑) 。
要するに
これ系の関数型風味のコレクション操作APIって割と確立されている。なので、いろんな言語やライブラリに似たようなものが存在していたりする。そういう意味でも、頭の片隅に置いておいて損はないのでは。みんな使っていこ。
underscore.js その後
2020年。IEが滅び、Webkit、Chromium系とFirefox系ブラウザの時代となった。Babelの登場もありクライアントサイドJSにおける悩みのタネは減る一方 (まぁ秒進分歩過ぎてフロントエンドエンジニアは違う意味でキツそう)。さらに、ES2015、ES2017の登場によりJavaScript標準のAPI自体も充実してきている。
このような状況の中、underscore.jsってもう要らんのでは?という議論もあるらしい。
おまけ
筆者はhaskellと和解できていません。
haskell - Does the chain function in underscore.js create a monad? - Stack Overflow
- 0. 関数型言語以外で使える、関数言語式のコレクション操作をまとめたライブラリの走りという意味で、エポックメイキングだったんだと個人的には認識している。
map()
,reduce()
といった個々の関数自体は関数型言語においてもともと一般的であったはず。↩