IT/web

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

어느해겨울 2021. 12. 28. 10:42

 

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, 배열 등으로 구성되기 때문에 코드가 약간 길어졌는데 순회식 검사를 통해 원하는 정보를 추출하였다.