Looping a multidimensional array to output a list on uniq numbers

Viewed 58

Given the following multidimensional array:

$menu = [
    'root' => [
        'items' => [
            'A' => [
                'p' => [1, 2, 9],
            ],
            'B' => [
                'p' => [1, 2, 3, 9],
            ],
            'C' => [
                'p' => [1, 2, 4, 9],
            ],
            'D' => [
                'items' => [
                    'D1' => [
                        'p' => [1, 2, 3, 4, 9],
                    ],
                    'D2' => [
                        'p' => [1, 2, 3, 4],
                    ],
                ],
            ],
            'E' => [
                'items' => [
                    'E1' => [
                        'p' => [1, 2, 10],
                    ],
                ],
            ],
            'F' => [
                'items' => [
                    'F1' => [
                        'p' => [5, 6],
                    ],
                    'F2' => [
                        'p' => [7, 8],
                    ],
                ],
            ],
        ],
    ],
];

Is there a way to get all the values in the 'p's as array, uniquely? The output should be [1, 2, 9, 3, 4, 10, 5, 6, 7, 8]

I tried a simple one-liner, but it works only for the first level (A, B, C), nested $items are ignored:

$ps = array_unique(call_user_func_array('array_merge', array_column($menu['root']['items'], 'p')));
print_r($ps);

I also tried to write a recursive function, but I get totally stuck and the output is not what's expected

function recursive_ps($elem, $arr = []){
    $output = $arr;
    if (isset($elem['items'])){
        foreach($elem['items'] as $key => $value){
            if (isset($value['p'])){
                $output = array_merge($arr, $value['p']);
                if (isset($value['items'])){
                    return recursive_ps($value, $output);
                }
            }
        }
    }
    return $output;
}
$o = recursive_ps($menu['root']);
print_r($o);

Please, any help?

1 Answers

The main issue is to return recursive_ps in the loop. You have to merge the returned data with the current array.

The second if to test $values['items'] shouldn't be inside the $value[p].

Also, $output = array_merge($arr, $value['p']); should be $output = array_merge($output, $value['p']); to combine to the current array.

Working code: (demo)

function recursive_ps($elem) 
{
    if (!isset($elem['items'])) {
        return [];
    }
    $output = [];
    foreach($elem['items'] as $key => $value) {
        if (isset($value['p'])) {
            $output = array_merge($output, $value['p']);
        }
        if (isset($value['items'])) {
            // merge the return value of function
            $output = array_merge($output, recursive_ps($value));
        }
    }
    return $output;
}

$output = array_values(array_unique(recursive_ps($menu['root'])));
print_r($output);

Output:

Array
(
    [0] => 1
    [1] => 2
    [2] => 9
    [3] => 3
    [4] => 4
    [5] => 10
    [6] => 5
    [7] => 6
    [8] => 7
    [9] => 8
)

Another version using reference :

function recursive_ps($elem, &$arr = []) {
    if (isset($elem['items'])) {
        foreach($elem['items'] as $key => $value) {
            if (isset($value['p'])) {
                $arr = array_merge($arr, $value['p']);
            }
            if (isset($value['items'])) {
                recursive_ps($value, $arr); // pass array by reference
            }
        }
    }
}

$output = [];
recursive_ps($menu['root'], $output); // pass array by reference
print_r(array_values(array_unique($output)));
Related