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 処理が行われます。
実際に利用する際は、この評価タイミングの違いは、意識しておく必要があります。

