PHP で配列の要素にアクセスして、処理を行うには、for や foreach を使うのがおなじみです。
この方法でも良いのですが、PHPには、それ以外にも配列を走査する関数やライブラリがあります。ここでは、配列を走査して処理を行う方法を見てみましょう。
サンプル仕様
このエントリで以下の配列を処理対象とします。array.php
で保存されている想定です。
<?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
を連結した文字列を格納する
求める結果は、以下になります。
array(2) { [0] => string(8) "2001Kato" [1] => string(10) "2009Aoyama" }
foreach を使う
まずは、foreach を使う方法です。よくある手続き的なPHPコードですね。foreachで配列を回して、year
が2000以上の場合だけ、結果配列に値を入れています。
<?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 行に分かれており、中間の結果を保持する一時変数が必要になるのが難点です。
<?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
が先に適用されるのと認識できるでしょうか。
$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
を実行します。
{ "require": { "illuminate/support": "~4.2" } }
Illuminate\Support\Collection
を使うことで、メソッドチェインで配列を操作することができます。
実装すると下記のようになります。filter
メソッドでフィルタリングを行い、その結果配列に対してmap
メソッドを実行して、結果配列の要素を作成していることが分かります。
<?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
を実行します。
{ "require": { "ginq/ginq": "~0.1" } }
Ginq を使って、実装すると、下記のようになります。配列を取り込むところ以外は、Illuminate\Support\Collection
と全く一緒になりました。
<?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 処理が行われます。
実際に利用する際は、この評価タイミングの違いは、意識しておく必要があります。