仙台の山奥で自転車に乗ったり転んだり

愛車の GIOS でサイクリングしたりポタリングしたり、それをブログに記録してみたり。ロードバイクや自転車や坂のことを書いてみたり。ときたまプログラムのことを忘れないようにメモってみたり。

配列のフィルタリング

これ読んで逐次納得できて、過去の苦い記憶がふつふつと思い浮かんだ。
http://qiita.com/nori0620/items/08bba8649fa5b608f695

array_filterのとこなんかは、イテレータとか使ったらもっとスマートになんないのかなと思ってゴニョゴニョしてみた。

これを、

$nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
$results = array_filter($nums, function ($i) {
    return (($i % 2) === 0);
});

// $results;
// => [
//        1 => 2,
//        3 => 4,
//        5 => 6,
//        7 => 8,
//        9 => 10
//    ]

// >>> array_values($results);
// => [
//        2,
//        4,
//        6,
//        8,
//        10
//    ]


PHPのドキュメントで「filter」「iterator」と検索したら「FilterIterator」が出てきた。
うーん……。これだけの用途なら、なんとも冗長でダルい。
あと、psyshでは無名クラスは扱えないぽい。

$nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
$iterator = new class(new \ArrayIterator($nums)) extends \FilterIterator {
    public function accept()
    {
        return (($this->getInnerIterator()->current() % 2) === 0);
    }
};

$results = iterator_to_array($iterator, false);
var_dump($results);

// array(5) {
//   [0]=>
//   int(2)
//   [1]=>
//   int(4)
//   [2]=>
//   int(6)
//   [3]=>
//   int(8)
//   [4]=>
//   int(10)
// }


なんか、もっと良い方法はないものかとPHPのドキュメントをもう少し探したら「CallbackFilterIterator」てのがあった。
これまた中々の冗長さだけど、さっきのよりはマシかな……。
イテレータの値にはキーが保持されているから、iterator_to_array関数で第二引数をFALSE(イテレータの要素のキーをインデックスとして使用しない)としないと、array_filter同様に配列は歯抜けになってしまうという罠があったりするけど。

$nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
$iterator = new \CallbackFilterIterator(new \ArrayIterator($nums), function ($i) {
    return (($i % 2) === 0);
});

// >>> iterator_to_array($iterator, false);
// => [
//        2,
//        4,
//        6,
//        8,
//        10
//    ]


あと、ジェネレータとか……。これはもう、目的からドンドン離れていってるな。

$nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
$gen = function($nums) {
    foreach ($nums as $i) {
        if (($i % 2) === 0) yield $i;
    }
};
$results = iterator_to_array($gen($nums));

>>> $results;
=> [
       2,
       4,
       6,
       8,
       10
   ]


(もっとスマートな方法はありそうだけど)収穫としては、配列のフィルタリングがしたいだけならarray_filter関数が一番良さそう。
でもarray_filter関数の戻り値は注意いしないと(連想配列の前提でいないと)思わぬバグを産む。
生産性高く安全な実装をしたいなら、やっぱりコレクションのライブラリを使ったほうがいいと再実感しました。
という結果かな。

あと、psyshで無名クラスが実行できないぽいことに気づきました。


蛇足。ジェネレータの短縮版……。

$nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
$gen = (function($nums) {
    foreach ($nums as $i) {
            if (($i % 2) === 0) yield $i;
        }
})($nums);
$results = iterator_to_array($gen);