説明
必要だったから探した。探したけど納得できるものが見つからなかったから書いた。車輪の再発明。劣化コピー万歳!
- 配列に対してarray_filterっぽいのを再帰的に行うarray_filter_recursive
- オブジェクトに対してarray_filterっぽいのを再帰的に行うobject_filter_recursive
- テストもあるよ
プログラム
/**
* 再帰的に配列をフィルタリングする
*
* @param array $array
* @param $callback
* @return array
*/
function array_filter_recursive($array, $callback): array {
if (! is_array($array)) {
trigger_error(__FUNCTION__.'(): Argument #1 ($array) must be of type array.', E_USER_ERROR);
}
if (! $callback instanceof \Closure) {
if (! function_exists($callback)) {
trigger_error(__METHOD__.'(): Argument #2 ($callback) callback function does not exist.', E_USER_ERROR);
}
}
foreach ($array as $key => $val) {
if (is_array($val)) {
$val = array_filter_recursive($val, $callback);
if (empty($val)) {
unset($array[$key]);
}
else {
$array[$key] = $val;
}
}
else if (is_object($val)) {
object_filter_recursive($val, $callback);
if (count(get_object_vars($val)) === 0) {
unset($array[$key]);
}
}
else {
if (! $callback($val)) {
unset($array[$key]);
}
}
}
return $array;
}
/**
* 再帰的にオブジェクトをフィルタリングする
*
* @param object $object
* @param $callback
* @return void
*/
function object_filter_recursive($object, $callback): void {
if (! is_object($object)) {
trigger_error(__FUNCTION__.'(): Argument #1 ($object) must be of type object.', E_USER_ERROR);
}
if (! function_exists($callback)) {
if (! $callback instanceof \Closure) {
trigger_error(__METHOD__.'(): Argument #2 ($callback) callback function does not exist.', E_USER_ERROR);
}
}
foreach ($object as $key => $val) {
if (is_array($val)) {
$val = array_filter_recursive($val, $callback);
if (empty($val)) {
unset($object->{$key});
}
else {
$object->{$key} = $val;
}
}
else if (is_object($val)) {
object_filter_recursive($val, $callback);
if (count(get_object_vars($val)) === 0) {
unset($object->{$key});
}
}
else {
if (! $callback($val)) {
unset($object->{$key});
}
}
}
}
テスト
/**
* @return void
*/
public function test_array_filter_recursive() {
//
// strlenが0の項目が削除され、空になったオブジェクトや配列が削除される事を確認
//
$array = [
'あ' => [
],
'い' => [
'a' => 'a',
],
'う' => [
'b' => 'b',
'c' => '',
],
'え' => [
'd' => '',
'e' => '',
],
'お' => '',
'か' => null,
'き' => 1,
'く' => new \stdClass(),
'け' => new \stdClass(),
'こ' => new \stdClass(),
];
$array['け']->f = 'f';
$array['こ']->g = '';
$actual = array_filter_recursive($array, 'strlen');
$expected = [
'い' => [
'a' => 'a',
],
'う' => [
'b' => 'b',
],
'き' => 1,
'け' => new \stdClass(),
];
$expected['け']->f = 'f';
$this->assertSame(serialize($expected), serialize($actual));
//
// nullの項目が削除される事を確認
//
$array = [
'a' => null,
'b' => 'not null',
'c' => '',
'd' => 0,
];
$actual = array_filter_recursive($array, function($val){
return ! is_null($val);
});
$expected = [
'b' => 'not null',
'c' => '',
'd' => 0,
];
unset($expected->b);
$this->assertSame($expected, $actual);
}
/**
* @return void
*/
public function test_object_filter_recursive() {
//
// 空のオブジェクトに何も変化がない事を確認
//
$object = new \stdClass();
$expected = clone $object;
object_filter_recursive($object, 'strlen');
$this->assertSame(serialize($expected), serialize($object));
//
// strlenが0の項目が削除され、空になったオブジェクトや配列が削除される事を確認
//
$object = new \stdClass();
$object->array_empty = [];
$object->array_not_empty = [0 => '0', 1 => '1', 2 => ''];
$object->object_empty = new \stdClass();
$object->object_not_empty = new \stdClass();
$object->object_not_empty->a = 'a';
$object->string_not_empty = 'b';
$object->string_empty = '';
$object->null = null;
$expected = clone $object;
object_filter_recursive($object, 'strlen');
unset($expected->array_empty);
unset($expected->array_not_empty[2]);
unset($expected->object_empty);
unset($expected->string_empty);
unset($expected->null);
$this->assertSame(serialize($expected), serialize($object));
// is_numericがfalseの項目が削除される事を確認
$object = new \stdClass();
$object->a = '1';
$object->b = 'a';
$expected = clone $object;
object_filter_recursive($object, 'is_numeric');
unset($expected->b);
$this->assertSame(serialize($expected), serialize($object));
}