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