Разница между array_map, array_walk и array_filter
В чем же разница между array_map
, array_walk
и array_filter
. Что я вижу из документации, так это то, что вы можете передать функцию обратного вызова для выполнения действия в поставляемом массиве. Но я, кажется, не обнаружил особых различий между ними.
Выполняют ли они то же самое?
Можно ли их использовать взаимозаменяемо?
Я был бы признателен за вашу помощь с иллюстративным примером, если они разные.
Ответы
Ответ 1
- Изменение значений:
-
array_map
не может изменять значения внутри входных массивов, в то время как array_walk
может; в частности, array_map
никогда не меняет своих аргументов.
- Доступ к массиву ключей:
- Возвращаемое значение:
-
array_map
возвращает новый массив, array_walk
возвращает только true
. Следовательно, если вы не хотите создавать массив в результате обхода одного массива, вы должны использовать array_walk
.
- Итерация нескольких массивов:
-
array_map
также может принимать произвольное количество массивов и может итерировать их параллельно, в то время как array_walk
работает только с одним.
- Передача произвольных данных в обратный вызов:
-
array_walk
может получить дополнительный произвольный параметр для передачи в обратный вызов. Это в основном не имеет отношения к PHP 5.3 (когда были введены анонимные функции).
- Длина возвращаемого массива:
- Результирующий массив
array_map
имеет ту же длину, что и длина самого большого входного массива; array_walk
не возвращает массив, но в то же время он не может изменить количество элементов исходного массива; array_filter
выбирает только подмножество элементов массива в соответствии с функцией фильтрации. Это сохраняет ключи.
Пример:
<pre>
<?php
$origarray1 = array(2.4, 2.6, 3.5);
$origarray2 = array(2.4, 2.6, 3.5);
print_r(array_map('floor', $origarray1)); // $origarray1 stays the same
// changes $origarray2
array_walk($origarray2, function (&$v, $k) { $v = floor($v); });
print_r($origarray2);
// this is a more proper use of array_walk
array_walk($origarray1, function ($v, $k) { echo "$k => $v", "\n"; });
// array_map accepts several arrays
print_r(
array_map(function ($a, $b) { return $a * $b; }, $origarray1, $origarray2)
);
// select only elements that are > 2.5
print_r(
array_filter($origarray1, function ($a) { return $a > 2.5; })
);
?>
</pre>
Результат:
Array
(
[0] => 2
[1] => 2
[2] => 3
)
Array
(
[0] => 2
[1] => 2
[2] => 3
)
0 => 2.4
1 => 2.6
2 => 3.5
Array
(
[0] => 4.8
[1] => 5.2
[2] => 10.5
)
Array
(
[1] => 2.6
[2] => 3.5
)
Ответ 2
Идея отображения функции в массив данных происходит из функционального программирования. Вы не должны думать о array_map
как о цикле foreach
который вызывает функцию для каждого элемента массива (даже если он так реализован). Это следует рассматривать как применение функции к каждому элементу в массиве независимо.
Теоретически такие вещи, как отображение функций, могут выполняться параллельно, поскольку функция, применяемая к данным, должна влиять ТОЛЬКО на данные, а НЕ на глобальное состояние. Это связано с тем, что array_map
может выбирать любой порядок, в котором применяется функция к элементам (хотя в PHP это не так).
array_walk
другой стороны, array_walk
- это совершенно противоположный подход к обработке массивов данных. Вместо того, чтобы обрабатывать каждый элемент отдельно, он использует состояние (&$userdata
) и может редактировать элемент на месте (подобно циклу foreach). Поскольку каждый раз, когда к элементу применяется $funcname
, он может изменить глобальное состояние программы, и для этого требуется единственный правильный способ обработки элементов.
В PHP, array_map
и array_walk
практически идентичны, за исключением того, что array_walk
дает вам больший контроль над итерацией данных и обычно используется для "изменения" данных на месте по сравнению с возвратом нового "измененного" массива.
array_filter
- это приложение array_walk
(или array_reduce
), и оно более или менее просто предоставлено для удобства.
Ответ 3
Из документации
bool array_walk (array & $array, callback $funcname [, mixed $userdata]) < -return bool
array_walk принимает массив и функцию F
и изменяет его, заменяя каждый элемент x на F(x)
.
массив array_map (обратный вызов обратного вызова, array $arr1 [, array $...]) < -return array
array_map делает то же самое кроме, что вместо изменения на месте он вернет новый массив с преобразованные элементы.
массив array_filter (массив $input [, callback $callback]) < -return array
array_filter с функцией F
вместо преобразования элементов удалят любые элементы, для которых F(x)
не является истинным
Ответ 4
Другие ответы демонстрируют разницу между array_walk (модификация на месте) и array_map (возврат измененной копии) достаточно хорошо. Тем не менее, они действительно не упоминают array_reduce, что является освещающим способом понимания array_map и array_filter.
Функция array_reduce принимает массив, функцию с двумя аргументами и "аккумулятор", например:
array_reduce(array('a', 'b', 'c', 'd'),
'my_function',
$accumulator)
Элементы массива объединены с аккумулятором один за раз, используя данную функцию. Результат вышеупомянутого вызова аналогичен этому:
my_function(
my_function(
my_function(
my_function(
$accumulator,
'a'),
'b'),
'c'),
'd')
Если вы предпочитаете думать в терминах циклов, это похоже на выполнение следующего (я фактически использовал это как резерв, когда array_reduce не был доступен):
function array_reduce($array, $function, $accumulator) {
foreach ($array as $element) {
$accumulator = $function($accumulator, $element);
}
return $accumulator;
}
Эта циклическая версия дает понять, почему я назвал третий аргумент "аккумулятором": мы можем использовать его для накопления результатов через каждую итерацию.
Итак, что это связано с array_map и array_filter? Оказывается, они оба являются особым видом array_reduce. Мы можем реализовать их следующим образом:
array_map($function, $array) === array_reduce($array, $MAP, array())
array_filter($array, $function) === array_reduce($array, $FILTER, array())
Игнорировать тот факт, что array_map и array_filter принимают свои аргументы в другом порядке; это просто еще одна причуда PHP. Важным моментом является то, что правая сторона идентична, за исключением функций, которые я назвал $MAP и $FILTER. Итак, как они выглядят?
$MAP = function($accumulator, $element) {
$accumulator[] = $function($element);
return $accumulator;
};
$FILTER = function($accumulator, $element) {
if ($function($element)) $accumulator[] = $element;
return $accumulator;
};
Как вы можете видеть, обе функции берут в накопитель $и возвращают его снова. Существуют две отличия в этих функциях:
- $MAP всегда будет добавляться к $аккумулятору, но $FILTER будет делать это только в том случае, если $function ($ element) имеет значение TRUE.
- $FILTER добавляет исходный элемент, но $MAP добавляет $function ($ element).
Обратите внимание, что это далеко не бесполезные мелочи; мы можем использовать его для повышения эффективности наших алгоритмов!
Мы часто видим код, похожий на эти два примера:
// Transform the valid inputs
array_map('transform', array_filter($inputs, 'valid'))
// Get all numeric IDs
array_filter(array_map('get_id', $inputs), 'is_numeric')
Использование array_map и array_filter вместо циклов делает эти примеры очень приятными. Однако это может быть очень неэффективно, если $input велико, так как первый вызов (карта или фильтр) будет пересекать $input и строить промежуточный массив. Этот промежуточный массив передается прямо во второй вызов, который будет пересекать все это снова, тогда промежуточный массив должен быть собран в мусор.
Мы можем избавиться от этого промежуточного массива, используя тот факт, что array_map и array_filter являются примерами array_reduce. Объединив их, нам нужно только пропустить $входы один раз в каждом примере:
// Transform valid inputs
array_reduce($inputs,
function($accumulator, $element) {
if (valid($element)) $accumulator[] = transform($element);
return $accumulator;
},
array())
// Get all numeric IDs
array_reduce($inputs,
function($accumulator, $element) {
$id = get_id($element);
if (is_numeric($id)) $accumulator[] = $id;
return $accumulator;
},
array())
ПРИМЕЧАНИЕ. Мои реализации array_map и array_filter выше не будут вести себя точно так же, как PHP, так как мой массив array_map может обрабатывать только один массив за один раз, и мой массив_filter не будет использовать "пустую" в качестве функции $по умолчанию. Кроме того, ни один из них не сохранит ключи.
Не сложно заставить их вести себя как PHP, но я чувствовал, что эти сложности затруднят определение основной идеи.
Ответ 5
В следующей редакции делается попытка более четко определить PHP array_filer(), array_map() и array_walk(), все из которых исходят из функционального программирования:
array_filter() отфильтровывает данные, создавая в результате новый массив, содержащий только нужные элементы прежнего массива, следующим образом:
<?php
$array = array(1, "apples",2, "oranges",3, "plums");
$filtered = array_filter( $array, "ctype_alpha");
var_dump($filtered);
?>
live code здесь
Все числовые значения отфильтровываются из массива $, оставляя $filter только с фруктами.
array_map() также создает новый массив, но в отличие от array_filter() результирующий массив содержит каждый элемент входного файла $filter, но с измененными значениями из-за применения обратного вызова к каждому элементу следующим образом:
<?php
$nu = array_map( "strtoupper", $filtered);
var_dump($nu);
?>
live code здесь
В этом случае код применяет обратный вызов с использованием встроенного strtoupper(), но определенная пользователем функция также является еще одним жизнеспособным вариантом. Обратный вызов применяется к каждому элементу $filter и тем самым порождает $nu, элементы которого содержат заглавные значения.
В следующем фрагменте массив walk() перемещает $nu и вносит изменения в каждый элемент по отношению к ссылочному оператору '&'. Изменения происходят без создания дополнительного массива. Каждое значение элемента изменяется на место в более информативную строку с указанием ее ключа, категории и значения.
<?php
$f = function(&$item,$key,$prefix) {
$item = "$key: $prefix: $item";
};
array_walk($nu, $f,"fruit");
var_dump($nu);
?>
См. демо
Примечание. Функция обратного вызова по отношению к array_walk() принимает два параметра, которые автоматически получат значение элемента и его ключ и в этом порядке также при вызове array_walk(). (Подробнее здесь).