array_walk_recursive с массивом?

#php #arrays #recursion

#php #массивы #рекурсия

Вопрос:

У меня есть массив меню, это многомерный массив, и я хочу что-то сделать с каждым отдельным элементом, поэтому я попробовал array_walk_recursive. Вот меню:

 $menu = array(
    array(
        'name'=>'a',
        'url'=>'b',
    ),
    array(
        'name'=>'c',
        'url'=>'d',
        'children'=>array(
            array(
                'name'=>'e',
                'url'=>'f'
            )
        )
    )
);
  

Но array_walk_recursive позволяет мне обрабатывать только каждый отдельный элемент, но не массив.

 array_walk_recursive($menu, function(amp;$item){
    var_dump($item);//return a;b;c;d;e;f;g
});
  

То, что я хочу, похоже на это:

 array_walk_recursive_with_array($menu, function(amp;$item){
    var_dump($item);//return array('name'=>'a','url'=>'b');a;b;array('name'=>'c',...);c;d;...
    if(is_array($item) amp;amp; isset($item['name'])){
        // I want to do something with the item.
    }
})
  

Существует ли какая-либо собственная реализация функции PHP?

Комментарии:

1. Может быть случай для RecursiveArrayIterator

2. array_walk_recursive заключается в изменении массива. каким бы вы хотели, чтобы ваш $menu был после array_walk_recursive выполнения.

Ответ №1:

согласно документации array_walk_recursive, вы не можете получить ключ внутреннего массива. Один из способов удовлетворить ваши потребности — использовать array_walk и создать собственную рекурсивность.

 function HandleArray(amp;$value){
    if(is_array($value)){
        //Do something with all array
        array_walk($value,'HandleArray');
    }
    else{
       //do something with your scalar value
    }
}
array_walk(array($your_array),'HandleArray');
  

Комментарии:

1. Правильно. Согласно PHP doc : «Любой ключ, содержащий массив, не будет передан функции». Почему это так, мне непонятно. Кажется нелогичным для функции с именем «recursive».

2. @mach128x Walking array-ключи, которые содержат массив рекурсивно, взорвутся, когда массивы внутри будут ссылаться на себя. Вам, по крайней мере, понадобится параметр maxDepth, чтобы остановить бесконечную рекурсию, прежде чем это произойдет. Приведенный выше пример HandleArray() также нуждается в защите от бесконечной рекурсии, если входной массив получен из неизвестного источника.

Ответ №2:

Вы хотите рекурсивно искать в своем массиве и выполнять некоторые действия каждый раз, когда вы сталкиваетесь с массивом, который содержит ключ name .

Для демонстрации приведем пример массива, который содержит неквалифицируемый подмассив ( $array[1] ) и три подходящих подмассива ( $array[0] , $array[2] и $array[2]['children'] ).

 $array = [
    [
        'name' => 'a',
        'url' => 'b',
    ],
    [
        'no-name' => 'foo',
    ],
    [
        'name' => 'c',
        'url' => 'd',
        'children' => [
            [
                'name' => 'e',
                'url' => 'f'
            ]
        ]
    ]
];
  

Чтобы обработать массив, выполните рекурсивный доступ к каждому узлу в структуре данных. При обнаружении массива используйте isset() (или array_key_exists() если вам нужно учитывать потенциальные null значения), чтобы проверить, подходит ли массив для желаемого действия.

Ниже приведен скрипт, который будет выполнять рекурсивную технику с соответствующими правилами и добавлять новый элемент всякий раз, когда правила будут выполнены. amp; используется в двух местах, чтобы указать, что переменная должна быть «изменена по ссылке» — это делает мутацию данных доступной за пределами области вызова пользовательской функции.

Код: (демо)

 function recurseAll(amp;$node) {
    if (is_array($node)) {
        if (isset($node['name'])) {
            // do something with the qualifying array...
            $node[] = 'push a new element into array level';
        }
        foreach ($node as amp;$childNode) {
            recurseAll($childNode);
        }
    }
}

recurseAll($array);
var_export($array);
  

Вывод:

 array (
  0 => 
  array (
    'name' => 'a',
    'url' => 'b',
    0 => 'push a new element into array level',      // added element to $array[0]
  ),
  1 => 
  array (                                            // didn't qualify
    'no-name' => 'foo',
  ),
  2 => 
  array (
    'name' => 'c',
    'url' => 'd',
    'children' => 
    array (
      0 => 
      array (
        'name' => 'e',
        'url' => 'f',
        0 => 'push a new element into array level', // added element to $array[2]['children']
      ),
    ),
    0 => 'push a new element into array level',     // added element to $array[2]
  ),
)
  

Ответ №3:

Защита от бесконечной рекурсии в примере с использованием @artragis array_walk() кажется необходимой. Пример класса ArrayHelper для уменьшения заданного массива до значений массива заданного ключа массива:

 class ArrayHelper {
    /**
     * arrayFlattenToKeyValues - Like finding keys with array_walk_recursive()
     *   without the restriction "Any key that holds an array will not be passed to the function"
     * @see https://www.php.net/manual/en/function.array-walk-recursive.php#refsect1-function.array-walk-recursive-examples
     * @param array $deepArray
     * @param string $fromKey
     * @param int $maxDepth default = 10
     * @param array $accumulator default = []
     *
     * @return array
     */
    public static function arrayFlattenToKeyValues(
        array $deepArray,
        string $fromKey,
        int $maxDepth = 10,
        array $accumulator = []
    ): array {
        array_walk($deepArray, function ($value, $key) use (amp;$accumulator, $fromKey, $maxDepth) {
            if ($key === $fromKey) {
                $accumulator[] = $value;
            }
            if ($maxDepth > 0 amp;amp; is_array($value)) {
                $accumulator = self::arrayFlattenToKeyValues($value, $fromKey, $maxDepth - 1, $accumulator);
            }
        });

        return $accumulator;
    }
}
  

Вероятно, это можно улучшить, упростить или сделать по-другому; пожалуйста, сделайте!