Diferencia entre array_map, array_walk y array_filter

373

¿Cuál es exactamente la diferencia entre array_map, array_walky array_filter. Lo que pude ver en la documentación es que puede pasar una función de devolución de llamada para realizar una acción en la matriz suministrada. Pero no parece encontrar ninguna diferencia particular entre ellos.

¿Realizan lo mismo?
¿Se pueden usar indistintamente?

Agradecería su ayuda con ejemplos ilustrativos si son diferentes en absoluto.

Gras Double
fuente
Este es un truco genial para el procesamiento de matriz con nombre a través de array_reduce (). Vale la pena leerlo si está investigando array_map, array_walk y array_filter. stackoverflow.com/questions/11563119/…
Lance Cleveland

Respuestas:

564
  • Cambio de valores:
    • array_mapno puede cambiar los valores dentro de la (s) matriz (s) de entrada mientras array_walkpuede; en particular, array_mapnunca cambia sus argumentos.
  • Acceso a teclas de matriz:
  • Valor de retorno:
    • array_mapdevuelve una nueva matriz, array_walksolo devuelve true. Por lo tanto, si no desea crear una matriz como resultado de atravesar una matriz, debe usarla array_walk.
  • Iterando matrices múltiples:
    • array_maptambién puede recibir un número arbitrario de matrices y puede iterar sobre ellas en paralelo, mientras array_walkopera solo en una.
  • Transferencia de datos arbitrarios a devolución de llamada:
    • array_walkpuede recibir un parámetro arbitrario adicional para pasar a la devolución de llamada. Esto es irrelevante desde PHP 5.3 (cuando se introdujeron funciones anónimas ).
  • Longitud de la matriz devuelta:
    • La matriz resultante array_maptiene la misma longitud que la matriz de entrada más grande; array_walkno devuelve una matriz, pero al mismo tiempo no puede alterar el número de elementos de la matriz original; array_filterselecciona solo un subconjunto de los elementos de la matriz de acuerdo con una función de filtrado. Conserva las llaves.

Ejemplo:

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

Resultado:

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
)
Artefacto
fuente
3
El manual de PHP dice: "array_walk (): solo los valores de la matriz pueden modificarse potencialmente";
feeela
10
"array_map no puede funcionar con las teclas de matriz" esto no es cierto:array_map(callback($key, $value), array_keys($array), $array)
Jarek Jakubowski
12
Todavía no está accediendo a las claves de ninguna matriz, está accediendo a los valores que coloca en una matriz que creó a partir de las claves. Es una solución alternativa, no niega la declaración.
inarilo
mientras que array_map no cambia implícitamente los valores, al asignar el resultado a la misma matriz básicamente lo cambia, y 'paradójicamente' array_walk que opera en la misma matriz no cambiará sus valores directamente, a menos que se pase el valor por referencia (matriz walk podría eliminar índices / elementos como array_filter indirectamente a través de una cláusula de uso de función anónima que pasa la matriz original, pero es una solución alternativa). Para concluir así, el cambio de valores, ni si un valor se devuelve o se pasa por referencia es de menor diferencia efectiva, pero el recorrido de matriz funciona con índices y mapa de matriz con múltiples matrices
FantomX1
además, parece que no importa que el recorrido de la matriz tome el primer parámetro de la matriz como referencia, cuando uno quiera cambiarlo, también debe pasar el valor del elemento de devolución de llamada como referencia
FantomX1
91

La idea de asignar una función a una matriz de datos proviene de la programación funcional. No debe pensar en array_mapun foreachbucle que llama a una función en cada elemento de la matriz (aunque así es como se implementa). Se debe considerar que aplica la función a cada elemento de la matriz de forma independiente.

En teoría, cosas como el mapeo de funciones se pueden hacer en paralelo ya que la función que se aplica a los datos SOLO debería afectar los datos y NO el estado global. Esto se debe a que array_mappodría elegir cualquier orden en el que aplicar la función a los elementos (aunque en PHP no lo hace).

array_walkpor otro lado, es exactamente el enfoque opuesto para manejar matrices de datos. En lugar de manejar cada elemento por separado, utiliza un estado ( &$userdata) y puede editar el elemento en su lugar (como un bucle foreach). Como cada vez que se $funcnameaplica un elemento , puede cambiar el estado global del programa y, por lo tanto, requiere una única forma correcta de procesar los elementos.

De vuelta en tierra PHP, array_mapy array_walkson casi idénticos, excepto que array_walkle da más control sobre la iteración de datos y normalmente se usa para "cambiar" los datos en el lugar frente a devolver una nueva matriz "cambiada".

array_filteres realmente una aplicación de array_walk(o array_reduce) y más o menos solo se proporciona por conveniencia.

Kendall Hopkins
fuente
55
+1 para su comprensión del segundo párrafo de "En teoría, cosas como el mapeo de funciones se pueden hacer en paralelo ya que la función que se aplica a los datos SOLO debería afectar los datos y NO el estado global". Para nosotros, los programadores paralelos, es algo útil a tener en cuenta.
etherice
¿Puedes explicar cómo array_filter()se puede implementar usando array_walk()?
pfrenssen
40

De la documentación,

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

array_walk toma una matriz y una función Fy la modifica reemplazando cada elemento x con F(x).

array array_map (callback $ callback, array $ arr1 [, array $ ...]) <- devolver array

array_map hace exactamente lo mismo, excepto que en lugar de modificar in situ devolverá una nueva matriz con los elementos transformados.

array array_filter (array $ input [, callback $ callback]) <- devolver array

array_filter con función F, en lugar de transformar los elementos, eliminará cualquier elemento para el que F(x)no sea cierto

Steven Schlansker
fuente
No se pudo entender por qué desaparecieron los valores de mi matriz. Mirando la documentación, supuse que array_walkdevolvió una matriz como array_mapy pensé que el problema estaba en mi función. No me di cuenta hasta que vi esto que el tipo de retorno es booleano.
Dylan Valade
22

Las otras respuestas demuestran la diferencia entre array_walk (modificación en el lugar) y array_map (devolución de copia modificada) bastante bien. Sin embargo, en realidad no mencionan array_reduce, que es una forma ilustrativa de comprender array_map y array_filter.

La función array_reduce toma una matriz, una función de dos argumentos y un 'acumulador', así:

array_reduce(array('a', 'b', 'c', 'd'),
             'my_function',
             $accumulator)

Los elementos de la matriz se combinan con el acumulador de uno en uno, utilizando la función dada. El resultado de la llamada anterior es el mismo que hacer esto:

my_function(
  my_function(
    my_function(
      my_function(
        $accumulator,
        'a'),
      'b'),
    'c'),
  'd')

Si prefiere pensar en términos de bucles, es como hacer lo siguiente (en realidad, he usado esto como una alternativa cuando array_reduce no estaba disponible):

function array_reduce($array, $function, $accumulator) {
  foreach ($array as $element) {
    $accumulator = $function($accumulator, $element);
  }
  return $accumulator;
}

Esta versión en bucle deja en claro por qué he llamado al tercer argumento "acumulador": podemos usarlo para acumular resultados a través de cada iteración.

Entonces, ¿qué tiene esto que ver con array_map y array_filter? Resulta que ambos son un tipo particular de array_reduce. Podemos implementarlos así:

array_map($function, $array)    === array_reduce($array, $MAP,    array())
array_filter($array, $function) === array_reduce($array, $FILTER, array())

Ignore el hecho de que array_map y array_filter toman sus argumentos en un orden diferente; Esa es solo otra peculiaridad de PHP. El punto importante es que el lado derecho es idéntico, excepto por las funciones que he llamado $ MAP y $ FILTER. Entonces, ¿cómo se ven?

$MAP = function($accumulator, $element) {
  $accumulator[] = $function($element);
  return $accumulator;
};

$FILTER = function($accumulator, $element) {
  if ($function($element)) $accumulator[] = $element;
  return $accumulator;
};

Como puede ver, ambas funciones toman el acumulador $ y lo devuelven nuevamente. Hay dos diferencias en estas funciones:

  • $ MAP siempre se agregará a $ acumulador, pero $ FILTER solo lo hará si $ function ($ element) es TRUE.
  • $ FILTER agrega el elemento original, pero $ MAP agrega $ function ($ element).

Tenga en cuenta que esto está lejos de ser una trivia inútil; ¡podemos usarlo para hacer que nuestros algoritmos sean más eficientes!

A menudo podemos ver códigos como estos dos ejemplos:

// Transform the valid inputs
array_map('transform', array_filter($inputs, 'valid'))

// Get all numeric IDs
array_filter(array_map('get_id', $inputs), 'is_numeric')

El uso de array_map y array_filter en lugar de bucles hace que estos ejemplos se vean bastante bien. Sin embargo, puede ser muy ineficiente si $ input es grande, ya que la primera llamada (mapa o filtro) atravesará $ input y creará una matriz intermedia. Esta matriz intermedia se pasa directamente a la segunda llamada, que atravesará todo nuevamente, luego la matriz intermedia tendrá que ser recolectada de basura.

Podemos deshacernos de esta matriz intermedia explotando el hecho de que array_map y array_filter son ejemplos de array_reduce. Al combinarlos, solo tenemos que atravesar $ input una vez en cada ejemplo:

// 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())

NOTA: Mis implementaciones de array_map y array_filter anteriores no se comportarán exactamente como PHP, ya que mi array_map solo puede manejar una matriz a la vez y mi array_filter no usará "vacío" como su función $ predeterminada. Además, ninguno conservará las claves.

No es difícil hacer que se comporten como PHP, pero sentí que estas complicaciones harían que la idea central fuera más difícil de detectar.

Warbo
fuente
1

La siguiente revisión busca delinear más claramente los array_filer (), array_map () y array_walk () de PHP, todos los cuales se originan en la programación funcional:

array_filter () filtra datos, produciendo como resultado una nueva matriz que contiene solo los elementos deseados de la matriz anterior, de la siguiente manera:

<?php
$array = array(1, "apples",2, "oranges",3, "plums");

$filtered = array_filter( $array, "ctype_alpha");
var_dump($filtered);
?>

código en vivo aquí

Todos los valores numéricos se filtran de $ array, dejando $ filtrado con solo tipos de fruta.

array_map () también crea una nueva matriz, pero a diferencia de array_filter (), la matriz resultante contiene todos los elementos de la entrada $ filtrada pero con valores alterados, debido a la aplicación de una devolución de llamada a cada elemento, de la siguiente manera:

<?php

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

código en vivo aquí

El código en este caso aplica una devolución de llamada utilizando el strtoupper incorporado () pero una función definida por el usuario es otra opción viable también. La devolución de llamada se aplica a cada elemento de $ filtrado y, por lo tanto, genera $ nu cuyos elementos contienen valores en mayúscula.

En el siguiente fragmento, array walk () atraviesa $ nu y realiza cambios en cada elemento con respecto al operador de referencia '&'. Los cambios ocurren sin crear una matriz adicional. El valor de cada elemento cambia en su lugar en una cadena más informativa que especifica su clave, categoría y valor.

<?php

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

Ver demo

Nota: la función de devolución de llamada con respecto a array_walk () toma dos parámetros que adquirirán automáticamente el valor de un elemento y su clave y, en ese orden, también cuando se invoque mediante array_walk (). (Ver más aquí ).

slevy1
fuente
1
Tenga en cuenta que las funciones $lambday $callbackson solo expansiones eta de funciones existentes, y por lo tanto son completamente redundantes. Puede obtener el mismo resultado al pasar (el nombre de) la función subyacente: $filtered = array_filter($array, 'ctype_alpha');y$nu = array_map('strtoupper', $filtered);
Warbo