Разница между array_map, array_walk и array_filter - PullRequest
354 голосов
/ 08 августа 2010

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

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

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

Ответы [ 5 ]

527 голосов
/ 08 августа 2010
  • Изменение значений:
    • array_map не может изменить значения внутри входных массивов, в то время как array_walk может; в частности, array_map никогда не изменяет свои аргументы.
  • Доступ к ключам массива:
    • array_map не может работать с ключами массива, array_walk может.
  • Возвращаемое значение:
    • array_map возвращает новый массив, array_walk возвращает только true. Следовательно, если вы не хотите создавать массив в результате обхода одного массива, вы должны использовать array_walk.
  • Итерация нескольких массивов:
    • array_map также может принимать произвольное количество массивов и может итерировать их параллельно, в то время как array_walk работает только с одним.
  • Передача произвольных данных для обратного вызова:
    • array_walk может получить дополнительный произвольный параметр для передачи в обратный вызов. Это в основном не имеет отношения к PHP 5.3 (когда были введены анонимные функции ).
  • Длина возвращаемого массива:
    • Полученный массив array_map имеет ту же длину, что и самый большой входной массив; array_walk не возвращает массив, но в то же время не может изменить количество элементов исходного массива; array_filter выбирает только подмножество элементов массива в соответствии с функцией фильтрации. Сохраняет ключи.
* * Пример тысяча семьдесят-девять:
<code><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; })
);

?>

Результат:

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
)
88 голосов
/ 08 августа 2010

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

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

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

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

array_filter на самом деле является приложением array_walk (или array_reduce) и более-менее просто предоставлено для удобства.

39 голосов
/ 08 августа 2010

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

bool array_walk (массив и $ массив, обратный вызов $ funcname [, mixed $ userdata]) <-retol bool </p>

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

массив array_map (обратный вызов $ callback, массив $ arr1 [, массив $ ...]) <- возвращаемый массив </p>

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

массив array_filter (массив $ input [, callback $ callback]) <- вернуть массив </p>

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

19 голосов
/ 01 мая 2014

Другие ответы достаточно хорошо демонстрируют разницу между 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 велико, так как первый вызов (map или filter) будет проходить по $ 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 может обрабатывать только один массив за раз, и мой array_filter не будет использовать «empty» в качестве функции $ по умолчанию. Кроме того, ни один из них не сохранит ключи.

Нетрудно заставить их вести себя как PHP, но я чувствовал, что эти сложности затруднят выявление основной идеи.

1 голос
/ 11 апреля 2011

В следующей редакции делается попытка более четко разграничить 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);
?>

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

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

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

<?php

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

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

Код в этом случае применяет обратный вызов, используя встроенную функцию strtoupper (), но пользовательская функция также является еще одним жизнеспособным вариантом. Обратный вызов применяется к каждому элементу $ filter и, следовательно, генерирует $ nu, элементы которого содержат значения в верхнем регистре.

В следующем фрагменте array walk () обходит $ nu и вносит изменения в каждый элемент в отношении оператора ссылки '&'. Изменения происходят без создания дополнительного массива. Значение каждого элемента изменяется на месте в более информативную строку с указанием его ключа, категории и значения.

<?php

$f = function(&$item,$key,$prefix) {
    $item = "$key: $prefix: $item";
}; 
array_walk($nu, $f,"fruit");
var_dump($nu);    
?>    

См. демо

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...