[php]多次元配列でもin_arrayを使えるようにする関数をクロージャを使って実装

More than 5 years have passed since last update.


in_arrayのfalse例

<?php

$array = array( 0 => array('id' => 1000),
1 => array('id' => 3000));
var_dump(in_array(3000, $array)); // bool(false)

配列の中にある値が入ってるか確認したい時に、in_arrayを使いたくても1次元配列までしか対応してないから上手く処理が出来ない。


in_arrayのtrue例

<?php

$array = array( 0 => array('id' => 1000),
1 => array('id' => 3000));
// 配列固有の処理
$list = array();
foreach ($array as $a) {
$list[] = $a['id'];
}
var_dump(in_array(3000, $list)); // bool(true)

上記のようにすれば実現出来るけど、こういう物は様々な場所で利用する事があるかもしれないから、出来れば共通に使えるように抽象化したい。

array_valuesは多次元配列に対応していないから、今回はクロージャを使って実装してみた。

useを使って自分自身を参照させ再帰処理出来るようにしてる。だからPHP5.3以降のみ対応

<?php

// 多次元配列のテストデータ
$test_array = array(array(
'aaaa'=> 'a',
'bbbb'=> array('b'),
'cccc'=> array(array('cccc'=>'c')),
'dddd'=> array(array(array('dddd'=>'d','eeee'=>'e'))),
'ffff'=>'f',
'gggg'=>array(array(array(array(array(array(array('g')))))))
),array(array('hhhh'=>array(array('iiii'=>array('h','i'))))));
array_push($test_array,'j','k');
$test_array['llll'] = 'l';

// array_valuesの再帰
function array_values_recursive ($a = array()) {
$r = function ($a) use (&$r) {
static $v = array();
foreach ($a as $ary) {
is_array($ary) ? $r($ary) : $v[] = $ary;
}
return $v;
};
return $r($a);
}
// テストデータからvalues値だけ取ってくる。
var_dump(array_values_recursive($test_array));

メモリ使用効率とか考えるともっともっと最適化できる気がする。


再帰処理前

array(5) {

[0]=>
array(6) {
["aaaa"]=>
string(1) "a"
["bbbb"]=>
array(1) {
[0]=>
string(1) "b"
}
["cccc"]=>
array(1) {
[0]=>
array(1) {
["cccc"]=>
string(1) "c"
}
}
["dddd"]=>
array(1) {
[0]=>
array(1) {
[0]=>
array(2) {
["dddd"]=>
string(1) "d"
["eeee"]=>
string(1) "e"
}
}
}
["ffff"]=>
string(1) "f"
["gggg"]=>
array(1) {
[0]=>
array(1) {
[0]=>
array(1) {
[0]=>
array(1) {
[0]=>
array(1) {
[0]=>
array(1) {
[0]=>
array(1) {
[0]=>
string(1) "g"
}
}
}
}
}
}
}
}
[1]=>
array(1) {
[0]=>
array(1) {
["hhhh"]=>
array(1) {
[0]=>
array(1) {
["iiii"]=>
array(2) {
[0]=>
string(1) "h"
[1]=>
string(1) "i"
}
}
}
}
}
[2]=>
string(1) "j"
[3]=>
string(1) "k"
["llll"]=>
string(1) "l"
}
こんな複雑な配列でも


再帰処理後

array(12) {

[0]=>
string(1) "a"
[1]=>
string(1) "b"
[2]=>
string(1) "c"
[3]=>
string(1) "d"
[4]=>
string(1) "e"
[5]=>
string(1) "f"
[6]=>
string(1) "g"
[7]=>
string(1) "h"
[8]=>
string(1) "i"
[9]=>
string(1) "j"
[10]=>
string(1) "k"
[11]=>
string(1) "l"
}

綺麗になりました(´ω`)

最初のin_arrayの例に適用するとこうなる。


in_arrayの例

<?php

function array_values_recursive ($a = array()) {
$r = function ($a) use (&$r) {
static $v = array();
foreach ($a as $ary) {
is_array($ary) ? $r($ary) : $v[] = $ary;
}
return $v;
};
return $r($a);
}
$array = array( 0 => array('id' => 1000),
1 => array('id' => 3000));
$array = array_values_recursive($array);
var_dump(in_array(3000, $array)); // bool(true)

今更ながら、in_arrayする分にはin_array側を再帰チェック出来るように拡張したほうが良かった気がしてきた・・・


in_array_recursive版

<?php

function in_array_recursive ($val, $a = array()) {
$r = function ($v,$a) use (&$r) {
static $bool = false;
if ($bool === true) return true;
foreach ($a as $ary) {
is_array($ary) && $r($v, $ary);
if ($v == $ary) {
$bool = true;
}
}
return $bool;
};
return $r($val, $a);
}
$array = array( 0 => array('id' => 1000),
1 => array('id' => 3000));
var_dump(in_array_recursive(3000, $array)); // bool(true)

valueが数値とかで小さければ、keyに置き換えて isset($list[$key])でチェック出来て尚良いかもしれない

っというかSPLのイテレータの中に

RecursiveArrayIterator

RecursiveIteratorIterator

あるじゃん\(^o^)/これ使えばもっとスマートに書けそうだ。なんかつかれた。