Разница между array_map, array_walk и array_filter

В чем же разница между array_map , array_walk и array_filter . Что я вижу из документации, так это то, что вы можете передать функцию обратного вызова для выполнения действия в поставляемом массиве. Но я, кажется, не обнаружил особых различий между ними.

Выполняют ли они то же самое?
Можно ли их использовать взаимозаменяемо?

Я был бы признателен за вашу помощь с иллюстративным примером, если они совсем разные.

  • array_map не имеет побочных эффектов, в то время как array_walk может; в частности, array_map никогда не меняет своих аргументов.
  • array_map не может работать с ключами массива, array_walk может.
  • array_map возвращает массив, array_walk возвращает true / false . Следовательно, если вы не хотите создавать массив в результате прохождения одного массива, вы должны использовать array_walk .
  • array_map также может принимать произвольное количество массивов, а array_walk работает только на одном.
  • 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 ) 

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

Теоретически такие вещи, как сопоставление функций, могут выполняться параллельно, поскольку функция, применяемая к данным, должна ТОЛЬКО влиять на данные и НЕ глобальное состояние. Это связано с тем, что array_map может выбрать любой порядок применения функции к элементам (хотя в PHP это не так).

array_walk с другой стороны, представляет собой совершенно противоположный подход к обработке массивов данных. Вместо того, чтобы обрабатывать каждый элемент отдельно, он использует состояние ( &$userdata ) и может редактировать элемент на месте (подобно циклу foreach). Поскольку каждый раз, когда элемент имеет $funcname применяемое к нему, он может изменять глобальное состояние программы, и для этого требуется один правильный способ обработки элементов.

Вернувшись в PHP land, array_map и array_walk почти идентичны, за исключением того, что array_walk дает вам больше контроля над итерацией данных и обычно используется для «изменения» данных на месте и возврата нового «измененного» массива.

array_filter – действительно приложение array_walk (или array_reduce ), и это более или менее просто предусмотрено для удобства.

Из документации,

bool array_walk (array & $ array, callback $ funcname [, mixed $ userdata]) <-return bool

array_walk принимает массив и функцию F и изменяет его, заменяя каждый элемент x на F(x) .

array array_map (callback $ callback, array $ arr1 [, array $ …]) <- возвращаемый массив

array_map делает то же самое, за исключением того, что вместо изменения на месте он вернет новый массив с преобразованными элементами.

array array_filter (массив $ input [, обратный вызов обратного вызова]) <- возвращаемый массив

array_filter с функцией F вместо преобразования элементов удалит любые элементы, для которых F(x) не является истинным

Другие ответы демонстрируют разницу между 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. Объединив их, нам нужно только пройти $ input один раз в каждом примере:

 // 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, но я чувствовал, что эти сложности затруднят определение основной идеи.

Следующая ревизия направлена ​​на более четкое определение массивов 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); ?> 

живой код здесь

Все числовые значения отфильтровываются из массива $, оставляя $ filter только с фруктами.

array_map () также создает новый массив, но в отличие от array_filter () результирующий массив содержит каждый элемент входного файла $ filter, но с измененными значениями из-за применения обратного вызова к каждому элементу следующим образом:

 <?php $nu = array_map( "strtoupper", $filtered); var_dump($nu); ?> 

живой код здесь

Код в этом случае применяет обратный вызов с использованием встроенного 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 (). (См. Больше здесь ).