【PHP】 よく使う配列を走査する関数
PHPには、forやforeach以外にも、他の言語でもあるようなmap、filter、findといった、配列を走査して特定の処理をする関数がいくつもあります。その中から、特によく利用するであろうものを紹介します。
forとforeachに関しては下の記事にあります。
array_map
mapは、配列のすべての要素に同じ操作を加えた新しい配列を作ります。元の配列は変化しません。
$ary = [1, 2, 3, 4]; $ary2 = array_map(fn ($v) => $v * 2, $ary); print(implode(", ", $ary2));
2, 4, 6, 8
上のコードはPHPのアロー関数を使用しています。
連想配列の場合、キーは元の配列のものが使用されます。
$dict = ["foo" => 100, "bar" => 200, "baz" => 300]; // 配列のキーは、元の配列のものが利用される $ary2 = array_map(fn ($v) => $v * 2, $dict); foreach ($ary2 as $k => $v) { print("$k: $v\n"); }
foo: 200 bar: 400 baz: 600
複数の同じ大きさの配列を使う
複数の同じ大きさの配列を同時に処理して新しい配列を作りたい場合、引数を増やします。
array_map(callback, 配列, 配列, 配列, ...);
のように書き、コールバック関数の引数の数も、その配列の数に合わせます。コールバック関数の実引数には、入れた配列の順で設定されます。
もし長さが異なる場合、存在しない要素のところには初期化されてない (issetがfalseになる) 値が入ります。
$ary = [1, 2, 3, 4]; $ary2 = [10, 20, 30, 40]; // array_map(callback, 配列, 配列, 配列, ...); $mapped = array_map(fn ($v1, $v2) => $v1 - $v2, $ary, $ary2); print(implode(", ", $mapped)); // -9, -18, -27, -36
-9, -18, -27, -36
$ary = [1, 2]; $ary2 = [10, 20, 30, 40]; $mapped = array_map(function ($v1, $v2) { print((isset($v1) ? "true" : "false") . "\n"); return $v1 - $v2; }, $ary, $ary2);
true true false false
array_filter
filterでは、特定の条件を満たす値を持つもののみを残した配列を新しく作ります。新しく作られる配列のインデックスは、連想配列の場合は値とキーのペアを引き継ぎます。
配列の場合、インデックスの振り直しがありません。そのため、filter後にarray_values
を実行するべきです。
引数はarray_filter(配列, callback)
になります。つまりmapと順が逆です。
$ary = [1, 2, 3, 4, 5]; $ary2 = array_filter($ary, fn($v) => $v % 2); // インデックスを振り直さない場合 foreach ($ary2 as $i => $v) { print("$i: $v\n"); } print("\n"); // インデックス振り直し $ary2 = array_values($ary2); foreach ($ary2 as $i => $v) { print("$i: $v\n"); }
0: 1 2: 3 4: 5 0: 1 1: 3 2: 5
$dict = ["foo" => 100, "bar" => 200, "baz" => 300]; $dict2 = array_filter($dict, fn($v) => $v % 200); foreach ($dict2 as $i => $v) { print("$i: $v\n"); }
foo: 100 baz: 300
もしキーと値の両方をコールバック関数に渡したい場合、第3引数にARRAY_FILTER_USE_BOTH
を設定します。
コールバックには、function (値, インデックス) {...}
のように入ります。
$ary = [10, 20, 30, 40, 50]; $ary2 = array_filter($ary, fn($v, $i) => $v % 20 && $i >= 1, ARRAY_FILTER_USE_BOTH); foreach ($ary2 as $i => $v) { print("$i: $v\n"); }
2: 30 4: 50
ARRAY_FILTER_USE_BOTH
は最初から定義されている定数です。
もしコールバックがない (array_filter(null, $ary)
と書いた) 場合、値がそのままif
の中に入るイメージになります。
つまり、値が0
、""
、null
、"0"
等の場合はfilterで除外されるということです。
array_walk
JavascriptのArray.forEach
のようなものです。すべての要素で同じ関数を呼び出します。この時、値が入る仮引数を参照にすると、配列の各値を書き換えることができます。
array_walk(&配列, callback, arg)
となります。callback
の引数はfunction (値, key, arg);
、walk
の第3引数のarg
は、callbackの第3引数に渡されるものです。
array_walk
自体の戻り値はtrue
のみです。
$dict = ["foo" => 100, "bar" => 200, "baz" => 300]; $ary = ["foo", "hige", "bar"]; array_walk($ary, function ($v, $i) use($dict) { if (isset($dict[$v])) { print("$v は\$dictに存在する\n"); } });
foo は$dictに存在する bar は$dictに存在する
// &$v のように & をつけると参照渡しになる array_walk($ary, function (&$v, $i, $prefix) { // 参照渡ししたため、配列の本体の値が変わる $v .= $prefix; }, "hoge"); print(implode(", ", $ary)); // aaahoge, bbbhoge, ccchoge
array_reduce
reduceは、配列を1つの値にまとめるものです。合計値を計算する、文字列を加工して連結する、新しい複雑な配列を作るなどの使い道があります。
array_reduce(配列, callback, 初期値);
のように書き、callback
はfunction (合計値, 配列の値) {...}
が入ります。
第3引数には初期値が入り、コールバックの第1引数には、その時点で積み上げた値が入ります。
$ary = [3, 1, 10, 6, 4]; $s = array_reduce($ary, function ($carry, $v) { print("$carry\n"); return $carry + $v; }, 0); print("合計: $s");
0 3 4 14 20 合計: 24
$ary = ["aaa", "bbb", "ccc"]; $ary2 = ["hoge", "fuga", "hige"]; $s = array_reduce($ary, function ($carry, $v) use($ary2) { $a2index = rand(0, count($ary2) - 1); // "aaa"と$ary2のどれかを連結、"bbb"と$ary2のどれかを連結…を繰り返す return $carry . "$v $ary2[$a2index]\n"; }, ""); print($s);
aaa hige bbb fuga ccc hige
その他の関数
かなりの数があるため、PHPの公式リファレンスを参照することをおすすめします。もし当てはまるものがない場合の最終手段が、普通のforやforeachであると考えて良いです。
まとめ
map
、filter
、reduce
、walk
あたりはしばしば使用する関数です。他にも数多くの関数があります。
配列を使って要素を置換するなど様々な関数があるため、PHPの公式を確認してみるのが良いです。
