it-swarm.com.ru

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

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

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

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

351
Web Logic
  • Изменение значений:
    • 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 выбирает только подмножество элементов массива в соответствии с функцией фильтрации. Это сохраняет ключи.

Пример:

<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
)
524
Artefacto

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

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

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

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

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

88
Kendall Hopkins

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

bool array_walk (array & $ array, обратный вызов $ funcname [ mixed $ userdata]) <-retol bool

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

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

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

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

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

39
Steven Schlansker

Другие ответы достаточно хорошо демонстрируют разницу между 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 добавляет функцию $ ($ 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, но я чувствовал, что эти сложности затруднят выявление основной идеи.

19
Warbo

В следующей редакции делается попытка более четко разграничить 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 (), но пользовательская функция также является еще одним приемлемым вариантом. Обратный вызов применяется к каждому элементу $ отфильтрованного и тем самым порождает $ 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 (). (Подробнее здесь ).

1
slevy1