Разница между картой массива, прогулкой массива и фильтром массива

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

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

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

5 ответов


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

идея картография функция к массиву данных приходит от функционального программирования. Вы не должны думать о 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) и его более или менее просто для удобства.


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

боол array_walk ( массив &$Array , то обратного вызова $имя_функции [, смешанный$, например, userdata ] )

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

использование array_map массив ( обратного вызова $обратного вызова , array $arr1 [, array $... ])

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

array array_filter ( array $input [, callback $callback ])

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;
};

как вы можете видеть, обе функции принимают $ accumulator и возвращают его снова. Есть два отличия в этих функциях:

  • $MAP всегда будет добавляться к $ accumulator, но $FILTER будет делать это только в том случае, если$function ($element) истинна.
  • $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 вместо циклов делает эти примеры довольно приятными. Однако это может быть очень неэффективно, если $ inputs велик, так как первый вызов (map или filter) будет проходить через $inputs и строить промежуточный массив. Этот промежуточный массив передается прямо во второй вызов, который будет проходить через все это снова, тогда промежуточный массив должен быть собран мусор.

мы можем избавиться от этого промежуточного массива, используя тот факт, что использование array_map и array_filter являются примерами array_reduce. Объединяя их, нам нужно только пересечь $inputs один раз в каждом примере:

// 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 не будет использовать "пустой" в качестве своей функции $по умолчанию. Кроме того, ни один не сохранит ключи.

нетрудно заставить их вести себя как 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);
?>

текущий код здесь

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

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

<?php

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

текущий код здесь

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