PHP で配列の要素にアクセスして、処理を行うには、for や foreach を使うのがおなじみです。
この方法でも良いのですが、PHPには、それ以外にも配列を走査する関数やライブラリがあります。ここでは、配列を走査して処理を行う方法を見てみましょう。
サンプル仕様
このエントリで以下の配列を処理対象とします。array.php
で保存されている想定です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php return [ [ 'id' => 1, 'year' => 1993, 'name' => 'Harada' , ], [ 'id' => 2, 'year' => 2001, 'name' => 'Kato' , ], [ 'id' => 3, 'year' => 2009, 'name' => 'Aoyama' , ] ]; |
この配列について処理を行います。
- 配列内に連想配列が格納されており、
name
とyear
というキーを持つ year
が、2000以上の要素のみ、結果配列に格納する- 結果配列には、
name
とyear
を連結した文字列を格納する
求める結果は、以下になります。
1 2 3 4 5 6 | array (2) { [0] => string(8) "2001Kato" [1] => string(10) "2009Aoyama" } |
foreach を使う
まずは、foreach を使う方法です。よくある手続き的なPHPコードですね。foreachで配列を回して、year
が2000以上の場合だけ、結果配列に値を入れています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?php $array = include ( 'array.php' ); $result = []; foreach ( $array as $v ) { if ( $v [ 'year' ] < 2000) { continue ; } $result [] = $v [ 'year' ] . $v [ 'name' ]; } var_dump( $result ); |
array系関数を使う
次に、filter と map を使って、実装します。PHP には、array_map
とarray_filter
関数があるので、これを使います。
実装は下記になります。array_filter
とarray_map
を使うので、それぞれ配列の要素をフィルタリングする、要素に処理を行い、結果配列を格納するといった意図がより明確になります。
ただ、array_fileter
とarray_map
で引数の順序が異なるのと、2 行に分かれており、中間の結果を保持する一時変数が必要になるのが難点です。
1 2 3 4 5 6 7 8 9 10 11 12 | <?php $array = include ( 'array.php' ); $tmp = array_filter ( $array , function ( $v ) { return $v [ 'year' ] >= 2000; }); $result = array_map ( function ( $v ) { return $v [ 'year' ] . $v [ 'name' ] ; }, $tmp ); var_dump( $result ); |
試しに 1 行にまとめると下記になります。一見良さそうですが、このコードをぱっと見て、array_filter
が先に適用されるのと認識できるでしょうか。
1 2 3 4 5 | $result = array_map ( function ( $v ) { return $v [ 'year' ] . $v [ 'name' ] ; }, array_filter ( $array , function ( $v ) { return $v [ 'year' ] >= 2000; })); |
Laravel(Illuminate\Supportパッケージ)を使う
filter / map を使う別の例として、Illuminate\Supportパッケージ のIlluminate\Support\Collection
クラスを使います。
https://github.com/illuminate/support
Illuminate\Support パッケージは、Laravel を構成しているパッケージの一つで、フレームワークを使わずとも、このパッケージ単体でも利用することができます。
インストールするには、composer.json
に以下のように指定して、composer install
もしくはcomposer update
を実行します。
1 2 3 4 5 | { "require": { "illuminate/support": "~4.2" } } |
Illuminate\Support\Collection
を使うことで、メソッドチェインで配列を操作することができます。
実装すると下記のようになります。filter
メソッドでフィルタリングを行い、その結果配列に対してmap
メソッドを実行して、結果配列の要素を作成していることが分かります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php use IlluminateSupportCollection; require_once __DIR__ . '/vendor/autoload.php' ; $array = include ( 'array.php' ); $result = Collection::make( $array )->filter( function ( $v ) { return $v [ 'year' ] >= 2000; // filter })->map( function ( $v ) { return $v [ 'year' ] . $v [ 'name' ]; // map })->toArray(); var_dump( $result ); |
Ginq を使う
Illuminate\Support\Collection
と似た機能を持つライブラリに、Ginq があります。こちらもメソッドチェインで配列への操作を行うことができます。
https://github.com/akanehara/ginq
インストールするには、composer.json
に以下のように指定して、composer install
もしくはcomposer update
を実行します。
1 2 3 4 5 | { "require": { "ginq/ginq": "~0.1" } } |
Ginq を使って、実装すると、下記のようになります。配列を取り込むところ以外は、Illuminate\Support\Collection
と全く一緒になりました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?php require_once __DIR__ . '/vendor/autoload.php' ; $array = include ( 'array.php' ); $result = Ginq::from( $array )->filter( function ( $v ) { return $v [ 'year' ] >= 2000; // filter })->map( function ( $v ) { return $v [ 'year' ] . $v [ 'name' ]; // map })->toArray(); var_dump( $result ); |
さいごに
4 つのパターンで配列を走査して、結果配列を求めるという処理を書いてみました。
foreach は、配列の要素を走査していくという汎用的な役割なので、そのループの中で様々な処理を書くことができます。一方、array系関数やライブラリは、それぞれのメソッドで用途や目的が決まっているので、どのような処理を行い、結果、どのような解を求めているのが分かりやすいです。
また、filter や map という概念は、多くのプログラミング言語で利用されており、こうした概念をおさえておくと、別の言語でコードを書いたり、読んだりする際に、意図を汲むことができ理解しやすくなります。
foreach で書くことがダメだというわけではなく、それ以外の書き方が、PHPにもあるということを知っておくということが大事ですね。
Illuminate\Support\Collection と Ginq
Illuminate\Support\Collection と Ginq は、Linq to Object ライクなインターフェースを持ち、実際に使い方も似ています。
ただ、この2つで大きく違うのが、評価のタイミングです。
Illuminate\Support\Collection は、map
メソッドを実行したタイミングで即時に評価され、処理が行われます。
一方、Ginqは、遅延評価となっており、map
メソッドを実行してもすぐに map 処理が行われません。このエントリの例では、toArray()
が実行されたタイミングで、はじめて map 処理が行われます。
実際に利用する際は、この評価タイミングの違いは、意識しておく必要があります。