PHPの多次元配列のkey => valueで検索する方法


147

多次元配列でキーと値のペアが見つかったすべてのサブ配列をすばやく取得する方法はありますか?配列の深さはわかりません。

単純な配列の例:

$arr = array(0 => array(id=>1,name=>"cat 1"),
             1 => array(id=>2,name=>"cat 2"),
             2 => array(id=>3,name=>"cat 1")
);

key = nameおよびvalue = "cat 1"を検索すると、関数は次を返すはずです。

array(0 => array(id=>1,name=>"cat 1"),
      1 => array(id=>3,name=>"cat 1")
);

関数は、最も深いレベルに到達するために再帰的である必要があると思います。

回答:


217

コード:

function search($array, $key, $value)
{
    $results = array();

    if (is_array($array)) {
        if (isset($array[$key]) && $array[$key] == $value) {
            $results[] = $array;
        }

        foreach ($array as $subarray) {
            $results = array_merge($results, search($subarray, $key, $value));
        }
    }

    return $results;
}

$arr = array(0 => array(id=>1,name=>"cat 1"),
             1 => array(id=>2,name=>"cat 2"),
             2 => array(id=>3,name=>"cat 1"));

print_r(search($arr, 'name', 'cat 1'));

出力:

Array
(
    [0] => Array
        (
            [id] => 1
            [name] => cat 1
        )

    [1] => Array
        (
            [id] => 3
            [name] => cat 1
        )

)

効率が重要な場合は$results、次のように配列をマージするのではなく、すべての再帰呼び出しが結果を同じ一時配列に格納するように記述できます。

function search($array, $key, $value)
{
    $results = array();
    search_r($array, $key, $value, $results);
    return $results;
}

function search_r($array, $key, $value, &$results)
{
    if (!is_array($array)) {
        return;
    }

    if (isset($array[$key]) && $array[$key] == $value) {
        $results[] = $array;
    }

    foreach ($array as $subarray) {
        search_r($subarray, $key, $value, $results);
    }
}

そこにあるキーは、search_r値ではなく参照によって4番目のパラメーターを受け取ります。アンパサンド&は重要です。

FYI:あなたはPHPの古いバージョンを持っているなら、あなたはで参照渡し一部を指定する必要があり、コールをsearch_r、その宣言ではなく。つまり、最後の行はになりsearch_r($subarray, $key, $value, &$results)ます。


2
@JohnKugelman $key配列に存在しない場合、「効率的な」回答エラーは発生し ませんか?それを行う方が良いif (array_key_exists($key, $array) && $array[$key] == $value) {でしょうか?
チェイス

1
@JohnKugelmanこの関数は、素敵な作品が、いつか私は私の持っている$valueあるnull...と機能が作業をしないarray empty場合でも、配列を持つことがどのように... $value= null?好きsearch($array, 'id', null)
Zagloo、2015

71

どうですか SPLの代わりにバージョン?入力の手間が省けます。

// I changed your input example to make it harder and
// to show it works at lower depths:

$arr = array(0 => array('id'=>1,'name'=>"cat 1"),
             1 => array(array('id'=>3,'name'=>"cat 1")),
             2 => array('id'=>2,'name'=>"cat 2")
);

//here's the code:

    $arrIt = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr));

 foreach ($arrIt as $sub) {
    $subArray = $arrIt->getSubIterator();
    if ($subArray['name'] === 'cat 1') {
        $outputArray[] = iterator_to_array($subArray);
    }
}

すばらしいのは、RecursiveArrayIteratorの代わりにRecursiveDirectoryIteratorを使用することにより、基本的に同じコードがディレクトリを反復処理することです。SPLはロクサーです。

SPLの唯一の厄介な点は、SPLがWebに正しく文書化されていないことです。しかし、いくつかのPHPの本には、いくつかの有用な詳細、特にPro PHPが記載されています。そして、あなたもおそらくもっと情報を得るためにググることができます。


これは魅力のように機能し、同様の問題に再度使用する予定です。最初はタイプミスだと思っていましたが、それは正しい方法です!Jaredに感謝します。
bchhun

2
素晴らしいソリューション。かなり速い!
TaylorOtwell、2011年

解決していただきありがとうございます。「id」はどこで取得できますか?$ outputArrayから?
トランテ

ありがとう、非常に簡単な解決策ですが、パフォーマンスについてはわかりません??。
Mahesh.D 2013

見つかった要素(サブ配列である可能性があります)を元の配列から設定解除する方法
Fr0zenFyr

49
<?php
$arr = array(0 => array("id"=>1,"name"=>"cat 1"),
             1 => array("id"=>2,"name"=>"cat 2"),
             2 => array("id"=>3,"name"=>"cat 1")
);
$arr = array_filter($arr, function($ar) {
   return ($ar['name'] == 'cat 1');
   //return ($ar['name'] == 'cat 1' AND $ar['id'] == '3');// you can add multiple conditions
});

echo "<pre>";
print_r($arr);

?>

参照:http : //php.net/manual/en/function.array-filter.php


4
これは、深さが1レベルしかない配列を検索する場合に適した解決策ですが、この特定の質問は、深い配列を再帰的に検索することに関するものでした(「関数は、最深レベルに到達するために再帰的である必要があります」)。
オード

16

これらの回答、特に上記のJohn Kugelmanの素晴らしい回答について最適化のヒントが必要な人のために、この更新を投稿するために戻ってきました。

彼の投稿された関数は正常に動作しましたが、12,000行の結果セットを処理するためにこのシナリオを最適化する必要がありました。関数は、すべてのレコードを通過するのに8秒かかりました。

検索を停止し、一致が見つかったときに戻るための関数が必要でした。つまり、customer_idを検索した場合、結果セットには1つしかないことがわかっているため、多次元配列でcustomer_idを見つけたら、戻りたいと考えています。

これは、この関数の速度が最適化された(そして大幅に簡略化された)バージョンです。他のバージョンとは異なり、配列の深さは1つしか処理できず、再帰せず、複数の結果をマージしません。

// search array for specific key = value
public function searchSubArray(Array $array, $key, $value) {   
    foreach ($array as $subarray){  
        if (isset($subarray[$key]) && $subarray[$key] == $value)
          return $subarray;       
    } 
}

これにより、12,000レコードを1.5秒に一致させるタスクがダウンしました。それでも非常に高価ですが、はるかに合理的です。


この1つは...このテストは私のケースや環境に固有の高速化(0.0020008087158203)...まあ対JHON /ジャレッドの答え(0.0009999275207519)よりもイムは、この、感謝のstefgosselinにこだわっ
Awena

14
if (isset($array[$key]) && $array[$key] == $value)

高速バージョンへのマイナーな改善。


2
実際、これにより、キーが設定されていないときに警告がスローされなくなります。それほどマイナーではありません!-> +1しました。
stefgosselin '10 / 06/05

2
私の意見では、重大なエラーのphpエラーログを実際に確認し、警告で汚染されないようにすることが同意されています。
codercake、2009

これは完全な解決策ではなく、「別の投稿への返信の試み」と「回答なし」のほうが多くなります。
mickmackusa

7

多次元配列では線形探索アルゴリズム(上記は線形)に注意してください。その深さは配列全体を走査するために必要な反復回数を増やすため、複雑になります。例えば:

array(
    [0] => array ([0] => something, [1] => something_else))
    ...
    [100] => array ([0] => something100, [1] => something_else100))
)

適切なアルゴリズムを使用して、(針が[100] [1]にある場合)探しているものを見つけるには、最大で200回の反復が必要です。

この場合の線形アルゴリズムはO(n)(配列全体の要素の総数)で実行されます。これは貧弱であり、100万のエントリ(たとえば、1000x100x10配列)は、針を見つけるために平均500,000回の反復が必要です。また、多次元配列の構造を変更する場合はどうなりますか?そして、PHPは、深度が100を超えると再帰アルゴリズムを実行します。

可能な場合は、常に多次元配列の代わりにオブジェクトを使用します。

ArrayObject(
   MyObject(something, something_else))
   ...
   MyObject(something100, something_else100))
)

カスタムコンパレータインターフェイスと関数を適用して、それらを並べ替えて見つけます。

interface Comparable {
   public function compareTo(Comparable $o);
}

class MyObject implements Comparable {
   public function compareTo(Comparable $o){
      ...
   }
}

function myComp(Comparable $a, Comparable $b){
    return $a->compareTo($b);
}

を使用uasort()して、カスタムコンパレーターを利用できます。冒険したい場合は、オブジェクトを並べ替えて管理できる独自のコレクションを実装する必要があります(少なくともArrayObjectを拡張して、少なくとも検索機能を含めるようにします)。

$arrayObj->uasort("myComp");

それらがソートされると(uasortはO(n log n)、任意のデータを取得するのと同じです)、バイナリ検索はO(log n)時間で操作を実行できます。探す。私の知る限り、カスタムコンパレータのバイナリ検索はPHPに実装されていません(array_search()私のプロパティではなくオブジェクト参照で機能する自然順序付けを使用同じように自分で実装する必要があります。

オブジェクトは並べ替え方法を定義するため、コードを無制限に再利用できるため、このアプローチはより効率的であり(深さはなくなりました)、さらに重要なことは普遍的です(インターフェースを使用して比較可能性を適用すると仮定)。はるかに良い=)


この答えは正しいはずです。ブルートフォース検索メソッドはそれを実行しますが、これははるかに少ないリソース集中型です。
2013年

同じ配列を何度も検索している場合にのみ、提案の意味が理解できることに注意してください。並べ替えの問題(O(n log n))を実行するのに、値(O(n))を単純に線形検索するよりもはるかに時間がかかります。しかし、並べ替えると、バイナリ検索の方が速くなります。
15年

また、配列の代わりにオブジェクトを使用することは有用な抽象化である可能性があることを追加する必要がありますが、配列がソートされている場合は、配列に対してバイナリ検索を実行することもできます。オブジェクトを使用して配列をソートしたり、配列をバイナリ検索したりする必要はありません。
15年

6

これが解決策です:

<?php
$students['e1003']['birthplace'] = ("Mandaluyong <br>");
$students['ter1003']['birthplace'] = ("San Juan <br>");
$students['fgg1003']['birthplace'] = ("Quezon City <br>");
$students['bdf1003']['birthplace'] = ("Manila <br>");

$key = array_search('Delata Jona', array_column($students, 'name'));
echo $key;  

?>

5
$result = array_filter($arr, function ($var) {   
  $found = false;
  array_walk_recursive($var, function ($item, $key) use (&$found) {  
    $found = $found || $key == "name" && $item == "cat 1";
  });
  return $found;
});


3
function in_multi_array($needle, $key, $haystack) 
{
    $in_multi_array = false;
    if (in_array($needle, $haystack))
    {
        $in_multi_array = true; 
    }else 
    {
       foreach( $haystack as $key1 => $val )
       {
           if(is_array($val)) 
           {
               if($this->in_multi_array($needle, $key, $val)) 
               {
                   $in_multi_array = true;
                   break;
               }
           }
        }
    }

    return $in_multi_array;
} 

私の場合は異なりますが、あなたの答えからヒントを得ました。
shyammakwana.me 2015年

2

私は似たようなものが必要でしたが、多次元配列を値で検索するために...私はジョンの例を使って書きました

function _search_array_by_value($array, $value) {
        $results = array();
        if (is_array($array)) {
            $found = array_search($value,$array);
            if ($found) {
                $results[] = $found;
            }
            foreach ($array as $subarray)
                $results = array_merge($results, $this->_search_array_by_value($subarray, $value));
        }
        return $results;
    }

私はそれが誰かを助けることを願っています:)


2

これは、John K.が投稿したものからの改訂された関数です...配列内の特定のキーのみを取得し、その上には何もない必要があります。

function search_array ( $array, $key, $value )
{
    $results = array();

    if ( is_array($array) )
    {
        if ( $array[$key] == $value )
        {
            $results[] = $array;
        } else {
            foreach ($array as $subarray) 
                $results = array_merge( $results, $this->search_array($subarray, $key, $value) );
        }
    }

    return $results;
}

$arr = array(0 => array(id=>1,name=>"cat 1"),
       1 => array(id=>2,name=>"cat 2"),
       2 => array(id=>3,name=>"cat 1"));

print_r(search_array($arr, 'name', 'cat 1'));

1

そして、値が見つかった配列要素からキー値を返す別のバージョン(再帰なし、速度に最適化):

// if the array is 
$arr['apples'] = array('id' => 1);
$arr['oranges'] = array('id' => 2);

//then 
print_r(search_array($arr, 'id', 2);
// returns Array ( [oranges] => Array ( [id] => 2 ) ) 
// instead of Array ( [0] => Array ( [id] => 2 ) )

// search array for specific key = value
function search_array($array, $key, $value) {
  $return = array();   
  foreach ($array as $k=>$subarray){  
    if (isset($subarray[$key]) && $subarray[$key] == $value) {
      $return[$k] = $subarray;
      return $return;
    } 
  }
}

ここに投稿したすべての人に感謝します。


1
function findKey($tab, $key){
    foreach($tab as $k => $value){ 
        if($k==$key) return $value; 
        if(is_array($value)){ 
            $find = findKey($value, $key);
            if($find) return $find;
        }
    }
    return null;
}

2
この答えについて詳しく教えてください。コードのみの回答は、実際に何をしているのかを説明していません。
リッチベナー2017年

教育する目的で質問を更新してください。
mickmackusa

これはキーを見つけるためだけに機能します。これは私にとってはうまくいきます。
Giovanny Gonzalez

0

キーの配列を検索したい場合、これは良いことです

function searchKeysInMultiDimensionalArray($array, $keys)
{
    $results = array();

    if (is_array($array)) {
        $resultArray = array_intersect_key($array, array_flip($keys));
        if (!empty($resultArray)) {
            $results[] = $resultArray;
        }

        foreach ($array as $subarray) {
            $results = array_merge($results, searchKeysInMultiDimensionalArray($subarray, $keys));
        }
    }

    return $results;
}

キー=>値の各セットは結果の配列の個別の配列になるため、キーは上書きされません。
キーを複製したくない場合は、これを使用してください

function searchKeysInMultiDimensionalArray($array, $keys)
{
    $results = array();

    if (is_array($array)) {
        $resultArray = array_intersect_key($array, array_flip($keys));
        if (!empty($resultArray)) {
            foreach($resultArray as $key => $single) {

                $results[$key] = $single;
            }
        }

        foreach ($array as $subarray) {
            $results = array_merge($results, searchKeysInMultiDimensionalArray($subarray, $keys));
        }
    }

    return $results;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.