본문 바로가기
IT/web

[PHP] array_walk_recursive와 배열 순회 탐색 소스코드 공유

by 어느해겨울 2021. 12. 28.

 

array_walk_recursive, 배열 순회 탐색, 전체 경로 찾기

 

array_walk_recursive

- 전체 배열을 포인터 위치와 상관없이 탐색한다.

- 모든 요소에 콜백 함수를 사용할 수 있다.

- 사용자가 정의한 함수를 재귀적 사용이 가능하다.

 

쉽게 얘기하면 인자로 주어진 배열을 구성하는 모든 요소의 마지막 key/value를 찾아내 준다.

그리고 각 요소에 대한 함수를 정의할 수 있다.

nested json format처럼 구조적으로 단순 패턴화 할 수 없는 경우 사용된다.

 

array_walk_recursive 포맷

array_walk_recursive(array|object &$array, callable $callback, mixed $arg = null): bool

 

array_walk_recursive 사용 예 #1, 기본

// https://www.php.net/manual/en/function.array-walk-recursive.php 예제 

<?php
  $sweet = array('a' => 'apple', 'b' => 'banana');
  $fruits = array('sweet' => $sweet, 'sour' => 'lemon');

  function test_print($item, $key)
  {
      echo "$key holds $item\n";
  }

  array_walk_recursive($fruits, 'test_print');
?>
a holds apple
b holds banana
sour holds lemon

 

array_walk_recursive 사용 예 #2, 내부 함수 및 use 사용

<?php
    $str_json = '{
                	"Document": {
                		"Root": {
                			"GroupViewList": {
                				"Attributes": {
                					"Version": "1.0"
                				},
                				"List": [{
                					"GroupIndex": 1,
                					"GroupName": "group",
                					"MemberZone": [{
                						"Number": 1,
                						"ZoneName": "zone",
                						"DeviceName": "device",
                						"IPAddress": "192.168.1.1"
                					}]
                				}]
                			}
                		}
                	}
                }';
    $arr_data = json_decode($str_json, true);
    
    $arr_last_key = [];
	array_walk_recursive($arr_data, function($_item, $_key) use(&$arr_last_key) {
		$arr_last_key[ $_key] = $_item;
	});
	
	var_dump($arr_last_key);
?>
array(7) {
  ["Version"]=>
  string(3) "1.0"
  ["GroupIndex"]=>
  int(1)
  ["GroupName"]=>
  string(5) "group"
  ["Number"]=>
  int(1)
  ["ZoneName"]=>
  string(4) "zone"
  ["DeviceName"]=>
  string(6) "device"
  ["IPAddress"]=>
  string(11) "192.168.1.1"
}

 

위의 예제 2의 경우는 마지막의 key와 value를 찾아주었다. 하지만 마지막 요소가 아닌 요소의 전체 경로의 구성을 파악하려면 다른 방법을 사용해야 한다. 그래서 본인이 사용하는 코드를 샘플로 공개한다.

 

배열 순회 경로 찾기 예

<?php
    $str_json = '{
        "Document": {
            "Root": {
                "GroupViewList": {
                    "Attributes": {
                        "Version": "1.0"
                    },
                        "List": [{
                            "GroupIndex": 1,
                            "GroupName": "group",
                            "MemberZone": [{
                                "Number": 1,
                                "ZoneName": "zone",
                                "DeviceName": "device",
                                "IPAddress": "192.168.1.1"
                            }]
                        }]
                }
            }
        }
    }';
    
    $arr_data  = [];
    $json_data = json_decode($str_json, true);
    
    $stack = &$json_data;
    while( $stack ) {
        list($key, $value) = each($stack);
        unset($stack[$key]);
    
        if( is_array($value) ) {
            $build = array($key => "__KEY__");
    
                if( count($value) == 0 ) {
                    $build["{$key}"] = "";
    
                } else {
                    $is_last_comma = false;
                    foreach( $value as $sub_key => $sub_value ) {
                        if( is_numeric($sub_value) ) {
                            $sub_value = strval($sub_value);
                        }
    
                        if( is_numeric($sub_key) && !is_array($sub_value) ) {
                            if( $build["{$key}"] == "__KEY__" ) $build["{$key}"] = "";
                            $build["{$key}"] = $build["{$key}"] . "{$sub_value},";
                            $is_last_comma = true;
    
                        }else {
                            $build["{$key}/{$sub_key}"] = $sub_value;
                        }
                    }
                    if( $is_last_comma ) {
                        $build["{$key}"] = rtrim($build["{$key}"], ",");
                    }
                }
            $stack = $build + $stack;
            continue;
    
        } else if( is_bool($value) ) {
            $value = json_encode($value);
        }
        $arr_data[$key] = $value;
    }
    
    var_dump($arr_data);
?>
array(15) {
  ["Document"]=>
  string(7) "__KEY__"
  ["Document/Root"]=>
  string(7) "__KEY__"
  ["Document/Root/GroupViewList"]=>
  string(7) "__KEY__"
  ["Document/Root/GroupViewList/Attributes"]=>
  string(7) "__KEY__"
  ["Document/Root/GroupViewList/Attributes/Version"]=>
  string(3) "1.0"
  ["Document/Root/GroupViewList/List"]=>
  string(7) "__KEY__"
  ["Document/Root/GroupViewList/List/0"]=>
  string(7) "__KEY__"
  ["Document/Root/GroupViewList/List/0/GroupIndex"]=>
  string(1) "1"
  ["Document/Root/GroupViewList/List/0/GroupName"]=>
  string(5) "group"
  ["Document/Root/GroupViewList/List/0/MemberZone"]=>
  string(7) "__KEY__"
  ["Document/Root/GroupViewList/List/0/MemberZone/0"]=>
  string(7) "__KEY__"
  ["Document/Root/GroupViewList/List/0/MemberZone/0/Number"]=>
  string(1) "1"
  ["Document/Root/GroupViewList/List/0/MemberZone/0/ZoneName"]=>
  string(4) "zone"
  ["Document/Root/GroupViewList/List/0/MemberZone/0/DeviceName"]=>
  string(6) "device"
  ["Document/Root/GroupViewList/List/0/MemberZone/0/IPAddress"]=>
  string(11) "192.168.1.1"
}

위 함수는 요소가 하위 배열을 보유하고 있는지, 값을 가지고 있는지 등등 조건을 파악하여 하위 요소를 갖는 key는 __KEY__라는 값을 넣어 child 요소가 있다는 것을 나타내고 그 이하 요소들은 요소까지 가는 모든 경로를 누적하여 전체 경로를 key로 사용하는 배열을 구성한다.

요소의 값이 boolean, 숫자 0, 배열 등으로 구성되기 때문에 코드가 약간 길어졌는데 순회식 검사를 통해 원하는 정보를 추출하였다.

 

 

댓글