LoginSignup
0
0

More than 1 year has passed since last update.

PHP array_filterを再帰的に行うarray_filter_recursiveを書いた

Last updated at Posted at 2022-05-04

説明

必要だったから探した。探したけど納得できるものが見つからなかったから書いた。車輪の再発明。劣化コピー万歳!

  1. 配列に対してarray_filterっぽいのを再帰的に行うarray_filter_recursive
  2. オブジェクトに対してarray_filterっぽいのを再帰的に行うobject_filter_recursive
  3. テストもあるよ

プログラム

/**
 * 再帰的に配列をフィルタリングする
 *
 * @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));
}
0
0
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
0
0