LoginSignup
64
59

More than 5 years have passed since last update.

array_map_recursiveはPHPに標準実装されていた!

Last updated at Posted at 2015-03-24

再帰の伴わない書き替え

問題

一次元配列$aの要素の値に全て1を加えたものを$bに代入せよ。

入出力例

$a
[1, 3, 2, 8, 99]
$b
[2, 4, 3, 9, 100]

解答例

一つずつ新しい配列に代入する

誰もが思いつくforeach構文を用いる方法です。

foreach構文を用いる
$b = [];
foreach ($a as $key => $value) {
    $b[$key] = $value + 1;
}

この用途ではarray_walk関数を使うメリットはいまいち無さそう…

array_walk関数を用いる
$b = [];
array_walk($a, function ($value, $key) use (&$b) {
    $b[$key] = $value + 1;
});

全体をコピーした後、1つずつ書き替える

PHPのforeach構文にはブロックスコープは存在しないため、ループを抜けた後に自分でunsetを実行しないと最後の要素に対するリファレンスがずっと続いてしまいます。

foreach構文を用いる
$b = $a;
foreach ($b as &$value) {
    ++$value;
}
unset($value);

最後に外側でunsetを呼ぶのが不恰好だと感じる場合、array_walk関数で解決です。

array_walk関数を用いる
$b = $a;
array_walk($b, function (&$value) {
    ++$value;
});

コールバック関数を適用する

関数脳の人ならこれ一択でしょう。私も大好きです。

array_map関数を用いる
$b = array_map(function ($value) {
    return $value + 1;
}, $a);

再帰の伴う書き替え

問題

任意の多次元配列$aの葉要素の値に全て1を加えたものを$bに代入せよ。

入出力例

$a
[[1], [[3]], [[[2]]], [8], 99]
$b
[[2], [[4]], [[[3]]], [9], 100]

解答例

is_array関数での判定を交えて手作業で再帰させる解答は省略します。ここではPHPの機能によって再帰させる方法のみを紹介します。

全体をコピーした後、1つずつ書き替える

array_walk関数はarray_walk_recursive関数の存在への布石としての意味合いが強いです。前者には 「それforeachで良くね?」 という疑問が沸きそうな感じはありましたが、後者には明確な再帰という目的があります。

array_walk_recursive関数を用いる
$b = $a;
array_walk_recursive($b, function (&$value) {
    ++$value;
});

コールバック関数を適用する

array_map_recursiveとかねーじゃん!?自分で作らないといけないんじゃないの?」 と思ったそこのあなた。実は…

filter_var関数の出番なんです。

まさかこんなところに出現するとは…

filter_var関数を用いる
$b = filter_var($a, FILTER_CALLBACK, ['options' => function ($value) {
    return $value + 1;
}]);

これを紹介するためだけにこの記事を書きました(笑

重要な追記

上記の例では+1しているため問題ありませんでしたが,$valueとして渡されてくる値は全てオプション無しのfilter_varを個別に通したような値になっています.すなわち,stringにキャストされるかfalseになっています

平坦化

問題

任意の多次元配列$aを平坦化して$bに代入せよ。キーは0からの連番整数を振りなおすものとする。

入出力例

$a
[[1], [[3]], [[[2]]], [8], 99]
$b
[1, 3, 2, 8, 99]

解答例

is_array関数での判定を交えて手作業で再帰させる解答は省略します。ここではPHPの機能によって再帰させる方法のみを紹介します。

一つずつ新しい配列に代入する

array_walk_recursive関数を使う方法が一番理解しやすいと思います。もちろんfilter_varでも実現することは出来ますが、今回はその返り値は使わずに逐次代入するだけなので、わざわざfilter_varを使う必要は無いでしょう。

array_walk_recursive関数を用いる
$b = [];
array_walk_recursive($a, function ($value) use (&$b) {
    $b[] = $value;
});

但し、以下のような目的がある場合はfilter_varのほうは配列もスカラー値も一貫して処理したい (配列でラップする必要がない)

再帰イテレータを用いる

RecursiveArrayIteratorクラスとRecursiveIteratorIteratorクラスを用いて再帰イテレータを作成した後、それをiterator_to_array関数を用いて配列に変換します。

RecursiveArrayIteratorクラスとRecursiveIteratorIteratorクラスを用いる
$b = iterator_to_array(new \RecursiveIteratorIterator(new \RecursiveArrayIterator($a)), false);

ちなみにこれらに関しても非再帰版のArrayIteratorクラスとIteratorIteratorクラスが再帰版への布石として存在します。

64
59
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
64
59