2014-12-23 4 views
2

배열에서 특정 키를 찾아서 그 값과 경로 인 을 반환하여 해당 키를 찾을 수 있습니다.. 예 :다차원 배열에서 키를 검색하고 경로를 반환하는 중

$array = array(
    'fs1' => array(
    'id1' => 0, 
    'foo' => 1, 
    'fs2' => array(
     'id2' => 1, 
     'foo2' => 2, 
     'fs3' => array(
     'id3' => null, 
    ), 
     'fs4' => array(
     'id4' => 4, 
     'bar' => 1, 
    ), 
    ), 
), 
); 

search($array, 'fs3'); // Returns ('fs1.fs2.fs3', array('id3' => null)) 
search($array, 'fs2'); // Returns ('fs1.fs2',  array('id2' => 1, ...)) 

나는 사용하여 데이터를 정확한 키를 찾을 수있는 배열을 재귀 적으로 반환 할 수있었습니다 RecursiveArrayIterator (아래 그림 참조),하지만 난 어떤 경로를 추적 할 수있는 가장 좋은 방법을 모르는 나는 현재 켜져있다.

$i = new RecursiveIteratorIterator 
    new RecursiveArrayIterator($array), 
    RecursiveIteratorIterator::SELF_FIRST); 
foreach ($i as $key => value) { 
    if ($key === $search) { 
    return $value; 
    } 
} 
+0

키는 항상 고유합니까? 모든 종류의 키가 하나의 차원에 들어갈 때 배열을 다차원으로 만드는 목적은 패배입니다. 첫 번째 인스턴스 또는 모든 배열을 반환하고 싶습니까? –

+0

@JonathanKuhn 키가 반드시 고유하지는 않지만 첫 번째 결과를 반환하는 것이 좋습니다. 검색 배열의 구조를 상속 했으므로 검색 배열을 변경하는 것은 옵션이 아닙니다. – nachito

+0

재귀 함수와 단순한 foreach 루프를 사용하여 진행 상황을 추적 할 수있는 것처럼 보입니다. –

답변

1

키가 항상 고유하다고 가정하는 것처럼 보입니다. 나는 그렇게 생각하지 않는다. 따라서 함수는 여러 값을 반환해야합니다. 내가 수행 할 작업은 단순히 재귀 함수를 작성하는 것입니다.

function search($array, $key, $path='') 
{ 
    foreach($array as $k=>$v) 
    { 
     if($k == $key) yield array($path==''?$k:$path.'.'.$k, array($k=>$v)); 
     if(is_array($v)) 
     { // I don't know a better way to do the following... 
      $gen = search($v, $key, $path==''?$k:$path.'.'.$k); 
      foreach($gen as $v) yield($v); 
     } 
    } 
} 

이것은 재귀 적 생성기입니다. 모든 히트를 포함하는 생성기를 반환합니다. 그것은 배열과 매우 비슷하게 사용됩니다 :

$gen = search($array, 'fs3'); 
foreach($gen as $ret) 
    print_r($ret); // Prints out each answer from the generator 
2

마치기를 위해서 그리고 미래의 방문자들. 위의 예제 코드와 내가 키를 얻으려는 해답에 대해 설명했다. 하나의 작은 변경으로 요청 된 결과를 반환하는 작동 함수가 있습니다. 반환 배열에서 키 대신 요청 된 0$search 대신 pathvalue 키를 반환합니다. 나는 이것을 더 장황하고 다루기 쉽다는 것을 알았다.

<?php 
$array = array(
    'fs1' => array(
     'id1' => 0, 
     'foo' => 1, 
     'fs2' => array(
      'id2' => 1, 
      'foo2' => 2, 
      'fs3' => array(
       'id3' => null, 
      ), 
      'fs4' => array(
       'id4' => 4, 
       'bar' => 1, 
      ), 
     ), 
    ), 
); 

function search($array, $searchKey=''){ 
    //create a recursive iterator to loop over the array recursively 
    $iter = new RecursiveIteratorIterator(
     new RecursiveArrayIterator($array), 
     RecursiveIteratorIterator::SELF_FIRST); 

    //loop over the iterator 
    foreach ($iter as $key => $value) { 
     //if the key matches our search 
     if ($key === $searchKey) { 
      //add the current key 
      $keys = array($key); 
      //loop up the recursive chain 
      for($i=$iter->getDepth()-1;$i>=0;$i--){ 
       //add each parent key 
       array_unshift($keys, $iter->getSubIterator($i)->key()); 
      } 
      //return our output array 
      return array('path'=>implode('.', $keys), 'value'=>$value); 
     } 
    } 
    //return false if not found 
    return false; 
} 

$searchResult1 = search($array, 'fs2'); 
$searchResult2 = search($array, 'fs3'); 
echo "<pre>"; 
print_r($searchResult1); 
print_r($searchResult2); 

출력 :

사용 느릅 RecursiveIteratorIterator에서 응답이 이미 존재
Array 
(
    [path] => fs1.fs2 
    [value] => Array 
     (
      [id2] => 1 
      [foo2] => 2 
      [fs3] => Array 
       (
        [id3] => 
       ) 

      [fs4] => Array 
       (
        [id4] => 4 
        [bar] => 1 
       ) 

     ) 

) 
Array 
(
    [path] => fs1.fs2.fs3 
    [value] => Array 
     (
      [id3] => 
     ) 

) 
+0

내 생명을 구했어. 고맙습니다!나는 많은 시간을 글쓰기/다시 쓰기 기능에 사용했으며,이 결과를 달성하기 위해 무엇을 하든지, 몇 시간 더 검색하면 찾았습니다. – xZero

1

. 내 솔루션이 항상 최적 일 수는 없을 것입니다. 예상 시간을 테스트하지 않았습니다. callHasChildren 메서드를 RecursiveIteratorIterator으로 다시 정의하여 최적화 할 수 있으므로 을 찾을 때 자식이 없어집니다. 그러나 이것은 도메인 밖에 있습니다. RecursiveIteratorIterator::CHILD_FIRST

function findKeyPathAndValue(array $array, $keyToSearch) 
{ 
    $iterator = new RecursiveIteratorIterator(
     new RecursiveArrayIterator($array), 
     RecursiveIteratorIterator::CHILD_FIRST 
    ); 

    $path = []; 
    $value = null; 
    $depthOfTheFoundKey = null; 
    foreach ($iterator as $key => $current) { 
     if (
      $key === $keyToSearch 
      || $iterator->getDepth() < $depthOfTheFoundKey 
     ) { 
      if (is_null($depthOfTheFoundKey)) { 
       $value = $current; 
      } 

      array_unshift($path, $key); 
      $depthOfTheFoundKey = $iterator->getDepth(); 
     } 
    } 

    if (is_null($depthOfTheFoundKey)) { 
     return false; 
    } 

    return [ 
     'path' => implode('.', $path), 
     'value' => $value 
    ]; 
} 

에주의 : 여기

당신이 명시 적으로 내부 루프를 사용할 필요가 없습니다 접근 방식이다. 이 플래그는 반복 순서를 바꿉니다. 따라서 우리는 하나의 루프 만 사용하여 경로를 준비 할 수 있습니다. 이것은 실제로 재귀 적 반복자의 주요 목적입니다. 그들은 당신에게서 모든 안쪽 고리를 숨 깁니다.

여기에 working demo입니다.

관련 문제