32
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

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

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^)/これ使えばもっとスマートに書けそうだ。なんかつかれた。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
32
Help us understand the problem. What are the problem?