web-dev-qa-db-fra.com

Différence entre array_map, array_walk et array_filter

Quelle est exactement la différence entre array_map, array_walk et array_filter. D'après ce que j'ai pu voir dans la documentation, vous pouvez utiliser une fonction de rappel pour effectuer une action sur le tableau fourni. Mais je ne semble pas trouver de différence particulière entre eux.

Est-ce qu'ils font la même chose?
Peuvent-ils être utilisés de manière interchangeable?

Je vous serais reconnaissant de votre aide avec exemple illustratif si elles sont différentes du tout.

356
Web Logic
  • Changement de valeurs:
    • array_map ne peut pas modifier les valeurs contenues dans un ou plusieurs tableaux d'entrée tandis que array_walk peut; en particulier, array_map ne change jamais ses arguments.
  • Accès aux touches de matrice:
  • Valeur de retour:
    • array_map renvoie un nouveau tableau, array_walk renvoie uniquement true. Par conséquent, si vous ne voulez pas créer un tableau après avoir traversé un tableau, vous devez utiliser array_walk .
  • Itération de plusieurs tableaux:
    • array_map peut également recevoir un nombre arbitraire de tableaux et peut les parcourir en parallèle, tandis que array_walk ne fonctionne que sur un seul.
  • Passage de données arbitraires au rappel:
    • array_walk peut recevoir un paramètre supplémentaire arbitraire à transmettre au rappel. Ceci est généralement sans importance depuis PHP 5.3 (quand fonctions anonymes ont été introduites).
  • Longueur du tableau retourné:
    • Le tableau résultant de _array_map_ a la même longueur que celui du tableau d'entrée le plus grand; _array_walk_ ne renvoie pas de tableau, mais il ne peut pas en même temps modifier le nombre d'éléments du tableau d'origine; array_filter ne sélectionne qu'un sous-ensemble des éléments du tableau en fonction d'une fonction de filtrage. Cela 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
)
_
530
Artefacto

L'idée de mapping une fonction pour un tableau de données provient de la programmation fonctionnelle. Vous ne devriez pas penser à array_map comme une boucle foreach qui appelle une fonction sur chaque élément du tableau (même si c'est ainsi qu'il est implémenté). Cela devrait être considéré comme une application de la fonction à chaque élément du tableau indépendamment.

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

array_walk d'autre part, c'est l'approche exactement opposée à la gestion des tableaux de données. Au lieu de gérer chaque élément séparément, il utilise un état (&$userdata) et peut modifier l'élément en place (un peu comme une boucle foreach). Étant donné que chaque fois qu'un élément se voit appliquer le $funcname, il peut changer l'état global du programme et nécessite par conséquent un seul moyen correct de traiter les articles.

De retour dans PHPland, 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. in-situ ou retour d'un nouveau tableau "modifié".

array_filter est vraiment une application de array_walk (ou array_reduce) et le fournit plus ou moins simplement pour des raisons de commodité.

88
Kendall Hopkins

De 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, array $ arr1 [ array $ ...]) <- retourne un tableau

array_map fait exactement la même chose à l'exception de qu'au lieu de modifier à la place, un nouveau tableau contenant les éléments transformés sera renvoyé.

array array_filter (array $ input [ callback $ callback]) <- retourne un tableau

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

39
Steven Schlansker

Les autres réponses montrent bien la différence entre array_walk (modification sur place) et array_map (retourne une copie modifiée). Cependant, ils ne mentionnent pas vraiment array_reduce, qui est une manière éclairante 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 à un, à l'aide de la fonction donnée. Le résultat de l'appel ci-dessus est identique à celui-ci:

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

Si vous préférez penser en termes de boucles, c'est comme si vous faisiez ceci:

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

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

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

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 le côté droit est identique, à l'exception des fonctions que j'ai appelées $ MAP et $ FILTER. Alors, à quoi ressemblent-ils?

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

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

Comme vous pouvez le constater, les deux fonctions récupèrent l’accumulateur $ et le renvoient à nouveau. Il existe deux différences entre ces fonctions:

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

Notez que c'est loin d'être une anecdote inutile; nous pouvons l'utiliser pour rendre nos algorithmes plus efficaces!

Nous pouvons souvent voir 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 array_filter au lieu de boucles donne à ces exemples un aspect plutôt sympathique. Cependant, cela peut être très inefficace si $ input est grand, car le premier appel (map ou filter) traversera $ input et construira un tableau intermédiaire. Ce tableau intermédiaire est passé directement dans le deuxième appel, qui traversera à nouveau le tout, puis le tableau intermédiaire devra être nettoyé.

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, il suffit de parcourir $ entrées une 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: Les implémentations de array_map et array_filter ci-dessus ne se comporteront pas exactement comme celles de PHP, car array_map ne peut gérer qu'un tableau à la fois et que array_filter n'utilise pas "empty" comme fonction $ par défaut. En outre, aucun ne conservera les clés.

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

19
Warbo

La révision suivante cherche à définir plus clairement les fonctions array_filer (), array_map () et array_walk () de PHP, toutes issues de la programmation fonctionnelle:

array_filter () élimine les données et crée un nouveau tableau contenant uniquement les éléments souhaités du tableau précédent, comme suit:

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

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

code live ici

Toutes les valeurs numériques sont filtrées de $ array, laissant $ filtré avec uniquement les types de fruits.

array_map () crée également un nouveau tableau, mais contrairement à array_filter (), le tableau résultant contient chaque élément de l'entrée $ filtré, mais avec des valeurs modifiées, en raison de l'application un rappel à chaque élément, comme suit:

<?php

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

code live ici

Dans ce cas, le code applique un rappel à l'aide de la fonction strtoupper () intégrée, mais une fonction définie par l'utilisateur est une autre option viable. Le rappel s'applique à chaque élément de $ filtré et engendre ainsi $ nu dont les éléments contiennent des valeurs majuscules.

Dans l'extrait suivant, array walk () parcourt $ nu et modifie chaque élément par rapport à l'opérateur de référence '&'. Les changements se produisent sans créer un tableau supplémentaire. La valeur de chaque élément change en place en une chaîne plus informative spécifiant sa clé, sa catégorie et sa valeur.

<?php

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

Voir démo

Remarque: la fonction de rappel relative à array_walk () prend deux paramètres qui acquièrent automatiquement la valeur de l'élément et sa clé, et dans cet ordre également lorsqu'ils sont appelés par array_walk (). (Voir plus ici ).

1
slevy1