413 votes

Différence entre array_map, array_walk et array_filter

Quelle est exactement la différence entre array_map , array_walk et array_filter . Ce que j'ai pu voir dans la documentation, c'est que vous pourriez passer une fonction de rappel pour effectuer une action sur le tableau fourni. Mais je ne semble pas trouver de différence particulière entre les deux.

Font-ils la même chose ?
Peuvent-ils être utilisés de manière interchangeable ?

J'apprécierais que vous m'aidiez en me donnant un exemple illustratif s'ils sont différents du tout.

Merci

639voto

Artefacto Points 50896
  • array_map n'a pas d'effets collatéraux tandis que array_walk peut ; en particulier, array_map ne change jamais ses arguments.
  • array_map ne peut pas agir sur les clés du tableau, array_walk peut.
  • array_map renvoie un tableau, array_walk uniquement les retours true / false . Par conséquent, si vous ne voulez pas créer un tableau à la suite de la traversée d'un tableau, vous devez utiliser la méthode suivante array_walk .
  • array_map peut également recevoir un nombre arbitraire de tableaux, alors que array_walk ne fonctionne que sur un seul.
  • array_walk peut recevoir un paramètre supplémentaire arbitraire à passer au callback. Ceci n'est plus pertinent depuis PHP 5.3 (quand fonctions anonymes ont été introduits).
  • Le tableau résultant de array_map / array_walk a le même nombre d'éléments que le ou les arguments ; array_filter ne prélève qu'un sous-ensemble des éléments du tableau en fonction d'une fonction de filtrage. Il préserve les clés.

Exemple :

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

Résultat :

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
)

99voto

Kendall Hopkins Points 12193

L'idée de cartographie une fonction à un tableau de données provient de la programmation fonctionnelle. Vous ne devriez pas penser à array_map en tant que foreach qui appelle une fonction sur chaque élément du tableau (même si c'est ainsi qu'elle est implémentée). Il faut penser qu'il s'agit d'appliquer la fonction à chaque élément du tableau indépendamment.

En théorie, des opérations telles que le mappage de fonctions peuvent être effectuées en parallèle, car la fonction appliquée aux données ne doit affecter QUE les données et NON l'état global. Ceci est dû au fait qu'un array_map pourrait choisir n'importe quel ordre dans lequel appliquer la fonction aux éléments (même si en PHP, ce n'est pas le cas).

array_walk d'autre part, il s'agit de l'approche exactement inverse de la manipulation des tableaux de données. Au lieu de traiter chaque élément séparément, elle utilise un état ( &$userdata ) et peut modifier l'élément en place (comme une boucle foreach). Puisque chaque fois qu'un élément a le $funcname appliqué, il pourrait changer l'état global du programme et nécessite donc un seul et unique correct manière de traiter les articles.

Retour au pays du PHP, array_map et array_walk sont presque identiques, sauf que array_walk vous donne plus de contrôle sur l'itération des données et est normalement utilisé pour "changer" les données sur place plutôt que de retourner un nouveau tableau "modifié".

array_filter est en fait une application de array_walk (ou array_reduce ) et il est plus ou moins fourni par commodité.

45voto

Steven Schlansker Points 17463

Dans la documentation,

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

array_walk prend un tableau et une fonction F et le modifie en remplaçant chaque élément x par F(x) .

array array_map ( callback $callback , tableau $arr1 [, tableau $... ] )<-retourner le tableau

tableau_map fait exactement la même chose sauf qu'au lieu de modifier in-place, il retournera un nouveau tableau avec les éléments transformés.

array_filter ( array $input [, callback $callback ]<-retour tableau

filtre_tableau avec fonction F au lieu de transformer les éléments, supprimera tous les éléments pour lesquels l'option F(x) n'est pas vrai

23voto

Warbo Points 502

Les autres réponses démontrent assez bien la différence entre array_walk (modification in-place) et array_map (retour de la copie modifiée). Cependant, elles ne mentionnent pas vraiment array_reduce, qui est un moyen éclairant de comprendre array_map et array_filter.

La fonction array_reduce prend un tableau, une fonction à deux arguments et un "accumulateur", comme ceci :

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

Les éléments du tableau sont combinés avec l'accumulateur un par un, en utilisant la fonction donnée. Le résultat de l'appel ci-dessus est le même que celui obtenu en faisant ceci :

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

Si vous préférez penser en termes de boucles, cela revient à faire ce qui suit (j'ai en fait utilisé ceci comme solution de repli lorsque array_reduce n'était pas disponible) :

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

Cette version en boucle montre clairement pourquoi j'ai appelé le troisième argument un "accumulateur" : nous pouvons l'utiliser pour accumuler les résultats à chaque itération.

Qu'est-ce que cela a à voir avec array_map et array_filter ? Il s'avère qu'ils sont tous deux un type particulier de array_reduce. Nous pouvons les implémenter comme suit :

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

Ignorez le fait que array_map et array_filter prennent leurs arguments dans un ordre différent ; c'est juste une autre bizarrerie de PHP. Le point important est que la partie droite est identique à l'exception des fonctions que j'ai appelées $MAP et $FILTER. Alors, à quoi ressemblent-elles ?

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

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

Comme vous pouvez le voir, les deux fonctions prennent le $accumulateur et le renvoient. Il y a deux différences dans ces fonctions :

  • $MAP sera toujours ajouté à $accumulator, mais $FILTER ne le fera que si $function($element) est TRUE.
  • $FILTER ajoute l'élément original, mais $MAP ajoute $function($element).

Notez qu'il s'agit loin d'être une futilité inutile ; nous pouvons l'utiliser pour rendre nos algorithmes plus efficaces !

On voit souvent du code comme ces deux exemples :

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

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

L'utilisation de array_map et de array_filter au lieu de boucles donne à ces exemples un aspect très agréable. Cependant, cela peut être très inefficace si $inputs est grand, puisque le premier appel (map ou filter) va parcourir $inputs et construire un tableau intermédiaire. Ce tableau intermédiaire est passé directement dans le second appel, qui va parcourir à nouveau l'ensemble du tableau, puis le tableau intermédiaire devra être ramassé.

Nous pouvons nous débarrasser de ce tableau intermédiaire en exploitant le fait que array_map et array_filter sont tous deux des exemples de array_reduce. En les combinant, nous n'avons à parcourir $inputs qu'une seule fois dans chaque exemple :

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

NOTE : Mes implémentations de array_map et de array_filter ci-dessus ne se comporteront pas exactement comme celles de PHP, puisque mon array_map ne peut gérer qu'un seul tableau à la fois et que mon array_filter n'utilisera pas "empty" comme fonction par défaut. De plus, aucun des deux ne préservera les clés.

Il n'est pas difficile de les faire se comporter comme ceux de PHP, mais j'ai pensé que ces complications rendraient l'idée principale plus difficile à repérer.

1voto

Sharon Levy Points 19
$array = array(1, "apples",2, "oranges",3, "plums");

// array_filter()
$lambda = function( $item ) {
if ( ctype_alpha($item) ) {
        return true;
    }
};
$filtered = array_filter( $array, $lambda);
var_dump($filtered);

// using array_map()
$callback = function($item) {
    return strtoupper($item);
};
$nu = array_map( $callback, $array);
var_dump($nu);

// array_walk()
$f = function(&$item,$key,$prefix) {
    $item = "$prefix: $item";
}; 
array_walk($array, $f, 'phun');
var_dump($array);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X