PHP foreach() с массивами внутри массивов?

Я хочу вызвать функцию для каждого элемента массива. Это, очевидно, очень просто с foreach(), но я начинаю разбирать, когда массивы содержат массивы. Может ли кто-нибудь помочь мне с функцией, которая будет выполнять некоторый код для каждой пары ключ -> значение из набора массивов внутри массивов. Теоретически глубина может быть бесконечной, но хорошим ограничением будет 3 итерации (массив в массиве в массиве), если рекурсия не работает.

В качестве примера можно взять массив из $_POST ниже:

Array
(
    [languages] => Array
    (
        [0] => php
        [1] => mysql
        [2] => inglip
    )

    [rates] => Array
    (
        [incall] => Array
        (
            [1hr] => 10
        )

        [outcall] => Array
        (
            [1hr] => 10
        )

    )
)

Просто чтобы убедиться, что я хочу запустить фрагмент кода (функцию), который передается каждому «конечному узлу» в структуре массива, поэтому в приведенном выше примере он будет вызываться, когда...

[0] => php
[1] => mysql
[2] => inglip
[1hr] => 10
[1hr] => 10

... найден.

Спасибо за любую помощь,

Джеймс


person Bojangles    schedule 02.04.2011    source источник
comment
(примечание) Правильным термином для конечного узла является лист.   -  person Gordon    schedule 02.04.2011
comment
также см. Получить все значения в одном массиве. Это не строго дубликат, но очень связанный.   -  person Gordon    schedule 02.04.2011
comment
Также взгляните на bool array_walk_recursive( ... ) =› php.net/manual/en/ function.array-walk-recursive.php   -  person jave.web    schedule 16.09.2015


Ответы (6)


Это идеальная работа для Итераторов:

$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
foreach($iterator as $key => $value) {
    echo "$key => $value\n";
}

См. Введение в итераторы SPL и Живое демо на codepad

EDIT: альтернативой может быть array_walk_recursive как показано в ответе Финбарра ниже

person Gordon    schedule 02.04.2011

См. array_walk_recursive — библиотечная функция PHP, которая рекурсивно вызывает определенный пользователем функция для предоставленного массива.

Из документов 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

Обратите внимание, что Any key that holds an array will not be passed to the function..

РЕДАКТИРОВАТЬ: немного менее уродливый пример:

<?php
$sweet = array('a' => 'apple', 'b' => 'banana');
$fruits = array('sweet' => $sweet, 'sour' => 'lemon');
array_walk_recursive($fruits, function ($item, $key) {
    echo "$key holds $item\n";
});
?>
person Finbarr    schedule 02.04.2011
comment
+1 Рядом с моим собственным ответом это единственный практический подход к проблеме. Все эти пользовательские рекурсивные функции просто смешны. Вы можете добавить небольшой пример использования, чтобы проиллюстрировать использование. - person Gordon; 02.04.2011
comment
Однако @Jam array_walk_recursive идеально подходит для вызова функции для каждого элемента в массиве. но ладно, я не буду жаловаться на принятый ответ :) Однако я отредактирую свой, чтобы включить здесь ссылку. Спасибо! - person Gordon; 02.04.2011
comment
С этой стороны претензий нет. Оба они были совершенно правильными ответами. Есть много способов решить одну и ту же проблему, и программист должен выбрать тот, который ему подходит. - person Finbarr; 02.04.2011
comment
Единственное небольшое затруднение, которое у меня есть с этим ответом, заключается в том, что я должен ссылаться на имя функции в виде строки. Для меня чище использовать функцию RecursiveIterator. - person Bojangles; 02.04.2011
comment
Что ж, как и в случае со всеми библиотечными функциями PHP, предназначенными для обратного вызова, вы можете передать анонимную функцию непосредственно в array_walk_recursive — вам не нужно использовать имя функции в виде строки. - person Finbarr; 03.04.2011

Как правило, в такой ситуации вам придется написать рекурсивную функцию -- что будет работать с элементом, если это не массив; и вызывать себя для элемента, если это массив.


Здесь у вас может быть что-то вроде этого:

$arr = array(
    'languages' => array(
        'php', 'mysql', 'inglip', 
    ), 
    'rates' => array(
        'incall' => array('1hr' => 10), 
        'outcall' => array('1hr' => 10), 
    ), 
);

function recurse($item) {
    foreach ($item as $key => $value) {
        if (is_array($value)) {
            recurse($value);
        } else {
            echo "$key : $value\n";
        }
    }
}


И вызов этой рекурсивной функции для вашего массива:

recurse($arr);

Достался бы тебе:

0 : php
1 : mysql
2 : inglip
1hr : 10
1hr : 10
person Pascal MARTIN    schedule 02.04.2011

Вот простая многоразовая реализация:

function recursive_foreach($array, $callback) {
    foreach($array as $key => $value) {
        if (is_array($value)) {
            recursive_foreach($value, $callback);
        }
        else {
            call_user_func($callback, $key, $value);
        }
    }
}

Где $callback — это обратный вызов, принимающий два аргумента: ключ и значение. Например:

recursive_foreach($array, function($k, $v) {
    echo "$k => $v<br>";
});
person Jon    schedule 02.04.2011
comment
Не знал, что PHP поддерживает анонимные функции! - person Finbarr; 02.04.2011
comment
@Finbarr @Jon PHP также поддерживал анонимные функции до версии 5.3 с функцией create_function. Это просто неудобно в использовании и медленно. - person Gordon; 02.04.2011

Я хочу вызвать функцию для каждого элемента массива. Это, очевидно, очень просто с foreach()

Если вы хотите запустить функцию для каждого элемента глубокого массива, вы можете использовать array_walk_recursive()

array_walk_recursive($array, function($v, $k)
{  
    echo "$k => $v\n";
});

Если вы хотите перебирать каждый элемент с помощью foreach, вам нужен рекурсивный итератор, как упоминалось ранее.

Если вы не уверены, какой из них использовать, используйте foreach + рекурсивный итератор. Это легче понять для большинства людей.

person Josh Davis    schedule 02.04.2011

person    schedule
comment
Да, это первое, что я придумал. Спасибо за усилия по публикации вашего ответа, но это, по меньшей мере, не идеальное решение. - person Bojangles; 02.04.2011