PHP에서 다차원 배열의 키 => 값으로 검색하는 방법


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네 번째 매개 변수를 값이 아닌 참조로 사용한다는 것입니다. 앰퍼샌드& 가 중요합니다.

참고 : 당신이 PHP의 이전 버전이있는 경우에 당신은에서 통과 참조에 의한 부분을 지정해야 호출search_r보다는 선언합니다. 즉, 마지막 줄은 search_r($subarray, $key, $value, &$results)입니다.


2
@JohnKugelman 어레이에 "효율적인"응답 오류 $key가 없습니까? 하는 것이 낫지 if (array_key_exists($key, $array) && $array[$key] == $value) {않습니까?
Chase

1
@JohnKugelman이 함수는 훌륭하게 작동하지만 언젠가는 $value어느 것이 null있고 함수가 작동하지 않습니다 ... array empty... $value= 경우에도 배열을 갖는 방법 null? 좋아 search($array, 'id', null)?
Zagloo

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에 대한 유일한 문제는 웹에 잘못 문서화되어 있다는 것입니다. 그러나 몇몇 PHP 서적은 특히 Pro PHP; 더 많은 정보를 얻으려면 Google을 사용하십시오.


이것은 매력처럼 작동하며 비슷한 문제에 대해 다시 사용할 계획입니다 .D 유일한 이상한 부분은 foreach에 있으며 $ sub 변수 대신 RecursiveIteratorIterator의 getSubIterator 함수를 사용하는 것입니다. 처음에는 오타라고 생각했지만 올바른 방법입니다! 감사합니다.
bchhun

2
멋진 해결책. 너무 빨리!
TaylorOtwell

솔루션 주셔서 감사합니다. "id"는 어디서 구할 수 있습니까? $ outputArray에서?
trante

고마워, 매우 직설적 인 솔루션이지만 성능에 대해 몰라 ??.
Mahesh.D

원래 배열에서 찾은 요소 (하위 배열 일 수 있음)를 설정 해제하는 방법은 무엇입니까?
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
이 방법은 한 수준에 불과한 배열을 검색하려는 경우에 좋은 솔루션이지만이 특정 질문은 깊은 배열에 재귀 적으로 검색하는 것입니다 ( "가장 깊은 수준으로 내려 가려면 함수가 재귀 적이어야합니다").
orrd

16

이 답변에 대한 최적화 팁이 필요한 사람, 특히 John Kugelman의 위의 훌륭한 답변을 위해이 업데이트를 게시했습니다.

그의 게시 된 기능은 정상적으로 작동하지만 12 000 행 결과 집합을 처리하기 위해이 시나리오를 최적화해야했습니다. 이 기능은 모든 레코드를 통과하는 데 8 초가 걸렸습니다.

검색을 중지하고 일치하는 것이 발견되면 반환하는 기능이 필요했습니다. 즉, customer_id를 검색하면 결과 집합에 하나만 있다는 것을 알고 다차원 배열에서 customer_id를 찾으면 반환하려고합니다.

다음은이 기능의 속도 최적화 및 훨씬 단순화 된 버전입니다. 다른 버전과 달리 배열의 한 가지 깊이 만 처리 할 수 ​​있고 재귀하지 않으며 여러 결과를 병합하지 않아도됩니다.

// 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 초로 일치시키는 작업이 중단되었습니다. 여전히 비용이 많이 들지만 훨씬 더 합리적입니다.


이 한 대 더 빨리 Jhon 이상 / 제라드의 대답 (0.0009999275207519)입니다 (0.0020008087158203) .. 그럼이 시험은 내 경우 환경 .. 임이 고집에 특정 감사 stefgosselin입니다
Awena

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

빠른 버전에 대한 사소한 개선.


2
실제로 이것은 키가 설정되지 않은 경우 경고를 발생시키지 않습니다. 그렇게 작지 않습니다! -> +1했습니다.
stefgosselin 2016 년

2
실제로 주요 오류에 대해 PHP 오류 로그를 한 눈에 볼 수 있고 경고로 오염되지 않는 것은 내 의견에 달려 있습니다.
codercake

이것은 완전한 솔루션이 아니며 "다른 게시물에 응답하려고 시도"및 "답변이 아님"에 가깝습니다.
mickmackusa 08

7

다차원 배열에서 선형 검색 알고리즘 (위의 선형)은 깊이가 전체 배열을 순회하는 데 필요한 반복 횟수를 증가 시키므로 복잡성이 복잡해 지므로주의하십시오. 예 :

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

적절한 알고리즘을 사용하여 원하는 것을 찾는 데 최대 200 회 반복해야합니다 (바늘이 [100] [1] 인 경우).

이 경우 선형 알고리즘은 O (n) (전체 배열의 총 요소 수 순서)에서 수행됩니다. 이는 불량한 것으로, 백만 개의 항목 (예 : 1000x100x10 배열)은 평균 500,000 회 반복하여 바늘을 찾습니다. 다차원 배열의 구조를 변경하기로 결정하면 어떻게 될까요? 그리고 깊이가 100을 초과하면 PHP는 재귀 알고리즘을 시작합니다. 컴퓨터 과학은 다음과 같이 더 잘할 수 있습니다.

가능하면 항상 다차원 배열 대신 객체를 사용하십시오.

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()속성이 아닌 객체 참조에서 작동하는 자연 순서 사용), 내가하는 것처럼 이것을 직접 구현해야합니다.

객체가 정렬 방식을 정의하므로 코드를 무한히 재활용 할 수 있으므로이 방법은 더 효율적이고 (더 이상 깊이는 없지만) 더 중요합니다 (인터페이스를 사용하여 비교 가능성을 적용한다고 가정). 훨씬 더 나은 =)


이 답변은 정확해야합니다. 무차별 대입 검색 방법으로 처리 할 수 ​​있지만 리소스 사용량이 훨씬 적습니다.
Drew

동일한 배열을 여러 번 검색하는 경우에만 제안하는 내용이 의미가 있습니다. 단순히 값 (O (n))에 대한 선형 검색을 수행하는 것보다 정렬 문제 (O (n log n))를 처리하는 데 훨씬 오래 걸립니다. 그러나 일단 정렬되면 바이너리 검색이 더 빠릅니다.
orrd

또한 배열 대신 객체를 사용하면 유용한 추상화가 될 수 있지만 배열이 정렬되어 있으면 배열에서 이진 검색을 수행 할 수도 있습니다. 객체를 사용하여 배열을 정렬하거나 이진 검색을 수행 할 필요는 없습니다.
orrd

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 12

2

비슷한 것이 필요했지만 값으로 다차원 배열을 검색하려면 John을 예로 들어 썼습니다.

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
이 답변을 확장 할 수 있습니까? 코드 전용 답변은 실제로 수행중인 작업을 설명하지 않습니다.
Rich Benner

교육하려는 의도로 질문을 업데이트하십시오.
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.