sumar manejo filtrar elementos array_walk array php arrays callback

php - manejo - Diferencia entre array_map, array_walk y array_filter



php array_walk (5)

¿Cuál es exactamente la diferencia entre array_map , array_walk y array_filter . Lo que pude ver en la documentación es que podría 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.


De la documentación,

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

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

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

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

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

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


La idea de mapping una función a una matriz de datos proviene de la programación funcional. No debe pensar en array_map como un bucle foreach que llama a una función en cada elemento de la matriz (aunque así se implementa). Se debe pensar que se aplica la función a cada elemento de la matriz de forma independiente.

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

array_walk por otro lado, es el enfoque exactamente opuesto al manejo de 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). Dado que cada vez que un elemento tiene el $funcname aplicado, puede cambiar el estado global del programa y, por lo tanto, requiere una única forma correcta de procesar los elementos.

De vuelta en PHP land, array_map y array_walk son casi idénticos, excepto que array_walk le da más control sobre la iteración de los datos y normalmente se usa para "cambiar" los datos in situ frente a devolver una nueva matriz "cambiada".

array_filter es realmente una aplicación de array_walk (o array_reduce ) y se proporciona más o menos para su comodidad.


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

array_filter () filtra los 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 here

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

array_map () también crea una nueva matriz, pero a diferencia de array_filter (), la matriz resultante contiene cada elemento 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 here

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

En el siguiente fragmento, array walk () atraviesa $ nu y realiza cambios en cada elemento con respecto al operador de referencia ''&''. Los cambios se producen 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 y la clave de un elemento y en ese orden, también cuando se invoque por array_walk (). (Ver más array_walk ).


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

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

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 prefieres pensar en términos de bucles, es como hacer lo siguiente (en realidad lo he usado 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 claro por qué llamé al tercer argumento un ''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; eso es sólo 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 de nuevo. Hay dos diferencias en estas funciones:

  • $ MAP siempre se agregará al $ acumulador, pero $ FILTER solo lo hará si $ function ($ element) es VERDADERO.
  • $ FILTER agrega el elemento original, pero $ MAP agrega la función $ ($ elemento).

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

A menudo podemos ver código 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 $ entradas es grande, ya que la primera llamada (mapa o filtro) atravesará $ entradas y construirá una matriz intermedia. Esta matriz intermedia se pasa directamente a la segunda llamada, la cual atravesará todo nuevamente, luego la matriz intermedia tendrá que ser recogida 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 $ entradas 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 las de PHP, ya que mi array_map solo puede manejar una matriz a la vez y mi array_filter no usará "empty" como su función predeterminada de $. Además, tampoco se conservarán las claves.

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


  • Valores cambiantes:
    • array_map no puede cambiar los valores dentro de la (s) matriz (s) de entrada mientras que array_walk puede; en particular, array_map nunca cambia sus argumentos.
  • Acceso a las teclas de matriz:
  • Valor de retorno:
    • array_map devuelve una nueva matriz, array_walk solo devuelve true / false . Por lo tanto, si no desea crear una matriz como resultado de atravesar una matriz, debe usar array_walk .
  • Iterando matrices múltiples:
    • array_map también puede recibir un número arbitrario de arreglos y puede iterar sobre ellos en paralelo, mientras que array_walk opera solo en uno.
  • Pasando datos arbitrarios a devolución de llamada:
    • array_walk puede recibir un parámetro arbitrario adicional para pasar a la devolución de llamada. Esto en su mayoría es irrelevante desde PHP 5.3 (cuando se introdujeron funciones anónimas ).
  • Longitud de la matriz devuelta:
    • La matriz resultante de array_map tiene la misma longitud que la de la matriz de entrada más grande; array_walk no devuelve una matriz, pero al mismo tiempo no puede alterar el número de elementos de la matriz original; array_filter selecciona 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 )