RecursiveIteratorIteratorIteratorIterator


(※画像はI love unicodeの人のスライドの「Code, Ideas, People」からの借用です。)


PHPの無名関数を使って再帰処理を行う : アシアルブログ

さて本題の再帰処理です。RubyのArray#flattenと同じようなものを実装してみます。Array#flattenはネストした配列を1次元の配列に平滑化するメソッドです。無名関数にする必要はないですが、再帰のサンプルと言うことで...

 $flatten = function($array) use(&$flatten) {
   $result = array();
  
   foreach ($array as $value) {
     if (is_array($value)) {
       // 配列ならこの無名関数を再度呼び出す
       $result = array_merge($result, $flatten($value));
     } else {
       $result[] = $value;
     }
   }
  
   return $result;
 };

こっちのほうが短いっすよ!

$flatten = function (array $a) {
    $results = array();
    array_walk_recursive($a, function ($v, $k) use (&$results) {
        //$results = array_merge($results, array($k => $v)); だとキーがあって重複したときにダメ
        $results = array_merge($results, (array)$v);
    });

    return $results;
};

$array = array('a' => 1, 5, array(3, array(22, 5, array(9, 500))), 12);
echo join (', ', $flatten($array)); //1, 5, 3, 22, 5, 9, 500, 12


PHP5.3で無名関数が搭載されたので、コールバックを使う頻度はあがると思います。(実際にlithiumには無名関数とarray_map、array_walkの組み合わせがいくつかあるし、ZFリードによるミニマルAOPPhly_PubSubだってコールバックありきだし、またBlankaでも!)
しかしながらブログ記事用のサンプルに突っ込むのは野暮ですけれども、無名関数をそのまま再起するのはモダンじゃない感じがします。ファイル読み込みや、多次元配列内のオブジェクトのメソッドを呼ぶ場合など、遅延評価なことを行うことも考えられます。再帰的に処理するわけなので、みんな大好きSPL系のRecursive群の出番ですね。

$array = new RecursiveIteratorIterator(new RecursiveArrayIterator($array),           
                                       RecursiveIteratorIterator::LEAVES_ONLY);

ちなみに、↑これをiterator_to_arrayするさいは各個別のarrayのキーはかぶってるので第2引数falseを加えないとだめです。

echo join(', ', iterator_to_array($array, false)); // 1, 5, 3, 22, 5, 9, 500, 12