Posted at

array_reduce を使い倒す

More than 3 years have passed since last update.


array_reduce を使い倒す


やりたいこと

配列の中から


  • 小文字ではじまる要素を抽出して

  • 先頭に "!" プレフィックスを付けたい


array_filter + array_map

function f1($array)

{
return array_map(function ($item) {
return '!' . $item;
}, array_filter($array, function ($item) {
return ctype_lower($item[0]);
}));
}
var_dump(f1(['Aa', 'bb', 'cC', 'DD']));
/*
array(2) {
[1] =>
string(3) "!bb"
[2] =>
string(3) "!cC"
}
*/

array_filter + array_map で実装したものです。

おそらく真っ先に頭に浮かぶ実装だと思いますし、何も間違っていないと思います。

ただ、php の array_*** 周りの引数は順番に統一性がなく、無名関数が出てくると途端に視認性が悪くなります。

例えば私は上の関数はパッと見「map してから filter する」ようにしか見えません。まず処理部分に視線が奪われるからです。

function f1_($array)

{
return array_map(array_filter($array, function ($item) {
return ctype_lower($item[0]);
}, function ($item) {
return '!' . $item;
}));
}

こう書ければいいのに array_map が空気を読んでいないんです(多分可変引数のためなんだろうけど)。

まぁそもそもワンライナーで書かなきゃ済む話なんですけどね。


array_reduce

function f2($array)

{
return array_reduce($array, function ($carry, $item) {
if (ctype_lower($item[0])) {
$carry[] = '!' . $item;
}
return $carry;
}, []);
}
var_dump(f2(['Aa', 'bb', 'cC', 'DD']));
/*
array(2) {
[0] =>
string(3) "!bb"
[1] =>
string(3) "!cC"
}
*/

array_reduce で実装したものです。

array_reduce はその関数名から配列→スカラー値にする関数に感じてしまいますが、別にそんなことはなく「今までの結果が引数で渡ってくる array_walk」のように捉えることが出来ます。

filter + map が1つの関数に集約され、個人的にはとても読みやすいです。

余談ですが、array_reduce 版だとキーが死んで数値連番になります。

が、こういった処理の場合、たいてい数値連番を期待していて、後で array_values をカマしたりするのでむしろ都合がいいくらいです。

ちなみにキーが死んで困る場合は array_keys + use でこうするのが好みです。

function f2_($array)

{
return array_reduce(array_keys($array), function ($carry, $item) use ($array) {
if (ctype_lower($array[$item][0])) {
$carry[$item] = '!' . $array[$item];
}
return $carry;
}, []);
}
var_dump(f2_(['Aa', 'bb', 'cC', 'DD']));
/*
array(2) {
[1] =>
string(3) "!bb"
[2] =>
string(3) "!cC"
}
*/


foreach

function f3($array)

{
$result = [];
foreach ($array as $key => $item) {
if (ctype_lower($item[0])) {
$result[$key] = '!' . $item;
}
}
return $result;
}
var_dump(f3(['Aa', 'bb', 'cC', 'DD']));
/*
array(2) {
[1] =>
string(3) "!bb"
[2] =>
string(3) "!cC"
}
*/

foreach で実装したものです。

ぶっちゃけ一番わかりや(ry


まとめ

個人的に array_reduce は結構好きな関数で、もっと使われて欲しいんですが、どうも


  • 不必要な参照、use してたり

  • array_sum, array_product などで十分だったり

なサンプルが多く、あんまりしっかり使われることがない印象です。

ので紹介も兼ねてこんな記事を書いてみました。

特に filter + map って相性が抜群で、非常にしばしば出てくる処理なので array_reduce で書き換えたくなったりします。

なお「文字列の1文字目を [0] でアクセス」してるのは単にサボってるだけです。

本題ではないのであまり突っ込まないでください。