PHP で配列を処理するとき、つい foreach で1要素ずつ書いてしまいがちですが、「全要素を変換する」「条件で絞り込む」「ひとつの値にまとめる」といった処理は、array_map・array_filter・array_reduce という3つの関数で短く・読みやすく書けます。この記事では、それぞれの基本的な使い方と、つまずきやすいポイント、foreach との使い分けまでを初心者向けに解説します。
目次
3つの関数でできること
まずは3つの関数が、それぞれどんな処理を担当するのかを整理しておきましょう。役割を一言でまとめると次のようになります。
| 関数 | 役割 |
|---|---|
array_map | 各要素を変換し、同じ要素数の新しい配列を返す |
array_filter | 条件に合う要素だけを残して絞り込む |
array_reduce | 全要素をひとつの値に畳み込む(合計・連結など) |
いずれも、各要素に対して行いたい処理を「コールバック関数」として渡すのが共通点です。元の配列を直接書き換えるのではなく、処理結果を新しい配列(または値)として返してくれるため、元データを壊さずに済むのも特徴です。
array_map:各要素を変換する
array_map は、配列のすべての要素にコールバック関数を適用し、その結果を新しい配列として返します。要素数は元の配列と変わりません。次は、各数値を2倍にする例です。
<?php
$numbers = [1, 2, 3, 4];
// 各要素を2倍にする
$doubled = array_map(function ($n) {
return $n * 2;
}, $numbers);
print_r($doubled);
// Array ( [0] => 2 [1] => 4 [2] => 6 [3] => 8 )
第1引数にコールバック関数、第2引数に元の配列を渡します。コールバックの戻り値が、そのまま新しい配列の各要素になります。元の $numbers は変更されません。
PHP 7.4 以降であれば、アロー関数を使ってさらに短く書けます。1行で完結する変換なら、こちらのほうが読みやすいことも多いです。
<?php
$numbers = [1, 2, 3, 4];
// アロー関数(PHP 7.4以降)で短く書く
$doubled = array_map(fn ($n) => $n * 2, $numbers);
print_r($doubled);
複数の配列を同時に処理する
array_map には配列を複数渡すこともできます。その場合、コールバックには各配列の同じ位置の要素がまとめて渡され、対応する要素どうしを組み合わせて処理できます。
<?php
$prices = [100, 200, 300];
$counts = [2, 1, 3];
// 単価 × 個数 を計算する
$totals = array_map(function ($price, $count) {
return $price * $count;
}, $prices, $counts);
print_r($totals);
// Array ( [0] => 200 [1] => 200 [2] => 900 )
array_filter:条件で絞り込む
array_filter は、コールバックが true を返した要素だけを残し、新しい配列として返します。次は、偶数だけを取り出す例です。
<?php
$numbers = [1, 2, 3, 4, 5, 6];
// 偶数だけを残す
$even = array_filter($numbers, function ($n) {
return $n % 2 === 0;
});
print_r($even);
// Array ( [1] => 2 [3] => 4 [5] => 6 )
ここで注意したいのが、引数の順番です。array_map は「コールバック → 配列」でしたが、array_filter は「配列 → コールバック」と逆になっています。混同しやすいので気をつけましょう。
コールバックを省略すると空の値が除かれる
array_filter は、コールバックを省略して配列だけを渡すこともできます。その場合は、各要素を真偽値として評価し、false と見なされる値(0・""・null・false・空配列など)を取り除きます。配列から「空っぽの値」をまとめて掃除したいときに便利です。
<?php
$values = [0, 1, '', 'hello', null, 'world', false];
// コールバックなし → 空とみなされる値を除去
$cleaned = array_filter($values);
print_r($cleaned);
// Array ( [1] => 1 [3] => hello [5] => world )
array_filter したらキーが飛び飛びになる問題
先ほどの実行結果をよく見ると、残った要素のキーが 1, 3, 5 のように飛び飛びになっています。これは array_filter が元の配列のキーをそのまま保持するためです。要素は取り除かれても、残った要素のキーは振り直されません。
多くの場合は問題ありませんが、0 から始まる連番のキーを期待してループや json_encode を行うと、意図しない結果になることがあります。たとえば連番キーでない配列を json_encode すると、配列ではなくオブジェクト({"1":2,...})として出力されてしまいます。
キーを 0 から振り直したいときは、array_values を通します。これで連番のインデックスにそろえられます。
<?php
$numbers = [1, 2, 3, 4, 5, 6];
$even = array_filter($numbers, fn ($n) => $n % 2 === 0);
// キーを 0 から振り直す
$even = array_values($even);
print_r($even);
// Array ( [0] => 2 [1] => 4 [2] => 6 )
array_reduce:ひとつの値に畳み込む
array_reduce は、配列の要素を順番に処理しながら、最終的にひとつの値へまとめあげる関数です。合計や最大値の計算、文字列の連結などに使います。コールバックは2つの引数を受け取ります。第1引数が「ここまでの計算結果(アキュムレータ)」、第2引数が「今回の要素」です。
<?php
$numbers = [1, 2, 3, 4];
// 合計を求める
$sum = array_reduce($numbers, function ($carry, $item) {
return $carry + $item;
}, 0); // 第3引数が初期値
echo $sum; // 10
第3引数の 0 が「初期値」です。最初のループでは $carry にこの初期値が入り、要素を処理するたびに戻り値が次の $carry へ引き継がれていきます。合計を求めるときは 0、掛け算なら 1、文字列の連結なら '' のように、処理に応じた初期値を指定します。
初期値を省略すると null から始まる
第3引数を省略すると、初期値は null になります。数値の合計を求める場合、null + 1 は 1 と評価されるため結果的に動きますが、配列が空のときに 0 ではなく null が返るなど、思わぬ挙動につながります。初期値は省略せず、明示的に指定するのがおすすめです。
文字列の連結の例も見てみましょう。初期値を空文字にしておくことで、安全に連結できます。
<?php
$words = ['PHP', 'is', 'fun'];
// スペース区切りで連結する
$sentence = array_reduce($words, function ($carry, $word) {
return $carry === '' ? $word : $carry . ' ' . $word;
}, '');
echo $sentence; // PHP is fun
foreach との使い分け
ここまでの処理は、すべて foreach でも書けます。では、どう使い分ければよいのでしょうか。判断の目安は「その処理が何をしたいのか」がコードを見てすぐ伝わるかどうかです。
array_map・array_filter・array_reduce は、関数名そのものが「変換」「絞り込み」「集計」という意図を表します。1要素ごとの単純な変換や絞り込み、集計であれば、これらを使ったほうが意図が明確で、元の配列を壊さない新しい配列が返るぶん安全です。
一方で、ループの途中で break や continue を使いたい場合、複数の処理を1つのループでまとめて行いたい場合、ログ出力やデータベース保存のように戻り値を必要としない副作用が中心の処理では、foreach のほうが素直に書けます。無理に高階関数へ詰め込むと、かえって読みにくくなることもあるので、ケースに応じて選びましょう。
まとめ
3つの関数は、配列処理の「定番の型」を短く表現するための道具です。最後に要点を振り返っておきましょう。
array_mapは各要素を変換し、同じ要素数の配列を返す(引数はコールバック → 配列)array_filterは条件で絞り込む(引数は配列 → コールバック)。キーは保持されるので必要ならarray_valuesで振り直すarray_reduceはひとつの値に畳み込む。初期値は省略せず明示する
「変換」「絞り込み」「集計」のどれをしたいのかを意識すると、自然と使う関数が決まってきます。まずは身近な配列を array_map で変換するところから試してみてください。