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.