php - Quick Recursive search of all indexes within an array ← (PHP)

Ok, so say I have an array as follows:

$buttons = array(
    'mlist' => array(
            'title' => 'Members',
            'href' => $scripturl . '?action=mlist',
            'show' => $context['allow_memberlist'],
            'sub_buttons' => array(
                'mlist_view' => array(
                    'title' => 'View the Member List',
                    'href' => $scripturl . '?action=mlist',
                    'show' => true,
                ),
                'mlist_search' => array(
                    'title' => 'Search for Members',
                    'href' => $scripturl . '?action=mlist;sa=search',
                    'show' => true,
                    'is_last' => true,
                ),
            ),
        ),
    'home' => array(
        'title' => 'Home',
        'href' => $scripturl,
        'show' => true,
        'sub_buttons' => array(
        ),
        'is_last' => $context['right_to_left'],
    ),
    'help' => array(
        'title' => 'Help',
        'href' => $scripturl . '?action=help',
        'show' => true,
        'sub_buttons' => array(
        ),
    ),
);

I need to sort through this array and return all indexes of it in another array as an index, and the values of these arrays will be the title. So it should return an array as follows:

array(
    'mlist' => 'Members',
    'mlist_view' => 'View the Member List',
    'mlist_search' => 'Search for Members',
    'home' => 'Home',
    'help' => 'Help',
);

How can this be achieved easily? Basically, need the key of each array if a title is specified and need to populate both within another array.

Answer



Solution:

The following snippet loops over all of the arrays (recursively) to extract the key/title pairs.

$index    = array();
$iterator = new RecursiveIteratorIterator(new ParentIterator(new RecursiveArrayIterator($buttons)), RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $key => $value) {
    if (array_key_exists('title', $value)) {
        $index[$key] = $value['title'];
    }
}
var_dump($index);

Answer



Solution:

How can this be achieved easily?

  1. initialize an empty, new array
  2. foreach the $buttons array with key and value
    1. extract title from value
    2. set the key in the new array with the title
  3. done.

Edit: In case a recursive array iterator catches too much (identifying elements as children while they are not - just being some other array), and you don't want to write an extension of the recursive iterator class, stepping through all children can be solved with some "hand written" iterator like this:

$index = array();
$childKey = 'sub_buttons';
$iterator = $buttons;
while(list($key, $item) = each($iterator))
{
    array_shift($iterator);
    $index[$key] = $item['title'];
    $children = isset($item[$childKey]) ? $item[$childKey] : false;
    if ($children) $iterator = $children + $iterator;
}

This iterator is aware of the child key, so it will only iterate over childs if there are some concrete. You can control the order (children first, children last) by changing the order:

if ($children) $iterator = $children + $iterator;
- or - 
if ($children) $iterator += $children;

Answer



Solution:

I'm sure my answer is not most efficient, but using many foreach loops and if checks, it can be done. However, with my solution if you nested another array inside of say 'mlist_view' that you needed to get a title from, it would not work. My solution works for a max of 2 arrays inside of arrays within buttons. A better (and more general purpose solution) would probably involve recursion.

$result = array();
foreach($buttons as $field => $value) {
    foreach($value as $nF => $nV) {
        if($nF === 'title') {
            $result[$field] = $nV;
        }
        if(is_array($nV)) {
            foreach($nV as $name => $comp) {
                if(is_array($comp)) {
                    foreach($comp as $nnF => $nnV) {
                        if($nnF === 'title') {
                            $result[$name] = $nnV;
                        }
                    } 
                }
            }
        }
    }
}
foreach($result as $f => $v) {
    echo $f.": ".$v."<br/>";
}

Answer



Solution:

This works for your value of $buttons, fairly simple:

function get_all_keys($arr) {
    if (!is_array($arr)) return array();
    $return = array();
    foreach (array_keys($arr) as $key) {
        if (is_array($arr[$key]) 
            && array_key_exists('title', $arr[$key]))
            $return[$key] = $arr[$key]['title'];
        $return = array_merge($return, get_all_keys($arr[$key]));
    }
    return $return;
}

echo "<pre>";
print_r(get_all_keys($buttons));
echo "</pre>";

Which returns:

Array
(
    [mlist] => Members
    [mlist_view] => View the Member List
    [mlist_search] => Search for Members
    [home] => Home
    [help] => Help
)

Source