【PHP】 よく使う配列を走査する関数

PHPには、forやforeach以外にも、他の言語でもあるようなmap、filter、findといった、配列を走査して特定の処理をする関数がいくつもあります。その中から、特によく利用するであろうものを紹介します。

array_map

mapは、配列のすべての要素に同じ操作を加えた新しい配列を作ります。元の配列は変化しません。

$ary = [1, 2, 3, 4];
$ary2 = array_map(fn ($v) => $v * 2, $ary);
print(implode(", ", $ary2));
result
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");
}
result
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
result
-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);
result
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");
}
result
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");
}
result
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");
}
result
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");
    }
});
result
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, 初期値);のように書き、callbackfunction (合計値, 配列の値) {...}が入ります。

第3引数には初期値が入り、コールバックの第1引数には、その時点で積み上げた値が入ります。

$ary = [3, 1, 10, 6, 4];
$s = array_reduce($ary, function ($carry, $v) {
    print("$carry\n");
    return $carry + $v;
}, 0);

print("合計: $s");
result
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);
result
aaa hige
bbb fuga
ccc hige

その他の関数

かなりの数があるため、PHPの公式リファレンスを参照することをおすすめします。もし当てはまるものがない場合の最終手段が、普通のforやforeachであると考えて良いです。

https://www.php.net/manual/ja/ref.array.php

まとめ

mapfilterreducewalkあたりはしばしば使用する関数です。他にも数多くの関数があります。

配列を使って要素を置換するなど様々な関数があるため、PHPの公式を確認してみるのが良いです。

php for thumb

役に立ったらシェアしよう!