Différence entre array_map, array_walk et array_filter

373

Quelle est exactement la différence entre array_map, array_walket array_filter. Ce que j'ai pu voir dans la documentation, c'est que vous pouvez 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 eux.

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

J'apprécierais votre aide avec un exemple illustratif s'ils sont différents du tout.

Gras Double
la source
C'est une astuce intéressante pour le traitement de tableaux nommés via array_reduce (). Vaut la peine d'être lu si vous étudiez array_map, array_walk et array_filter. stackoverflow.com/questions/11563119/…
Lance Cleveland

Réponses:

564
  • Modification des valeurs:
    • array_mapne peut pas modifier les valeurs à l'intérieur des tableaux d'entrée pendant que array_walkcan; en particulier, array_mapne change jamais ses arguments.
  • Accès aux clés de tableau:
  • Valeur de retour:
    • array_maprenvoie un nouveau tableau, array_walkne renvoie que true. Par conséquent, si vous ne souhaitez pas créer un tableau suite à la traversée d'un tableau, vous devez utiliser array_walk.
  • Itération de plusieurs tableaux:
    • array_mappeut également recevoir un nombre arbitraire de tableaux et il peut les parcourir en parallèle, alors qu'il array_walkne fonctionne que sur un seul.
  • Passage de données arbitraires au rappel:
    • array_walkpeut recevoir un paramètre arbitraire supplémentaire pour passer au rappel. Cela est pratiquement hors de propos depuis PHP 5.3 (lorsque des fonctions anonymes ont été introduites).
  • Longueur du tableau retourné:
    • Le tableau résultant de array_mapa la même longueur que celui du plus grand tableau d'entrée; array_walkne renvoie pas de tableau mais en même temps il ne peut pas modifier le nombre d'éléments du tableau d'origine; array_filtersélectionne uniquement un sous-ensemble des éléments du tableau en fonction d'une fonction de filtrage. Il conserve 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
)
Artefacto
la source
3
Le manuel PHP dit: "array_walk (): Seules les valeurs du tableau peuvent potentiellement être modifiées;"
feeela
10
"array_map ne peut pas fonctionner avec les clés du tableau" ce n'est pas vrai:array_map(callback($key, $value), array_keys($array), $array)
Jarek Jakubowski
12
Ce n'est toujours pas accéder aux clés d'un tableau, c'est accéder aux valeurs que vous mettez dans un tableau que vous avez créé à partir des clés. C'est une solution de contournement, cela ne nie pas la déclaration.
inarilo
tandis que array_map ne modifie pas implicitement les valeurs, en affectant le résultat au même tableau, il le change fondamentalement, et `` paradoxalement '' array_walk qui opère sur le même tableau lui-même ne changera pas ses valeurs directement, à moins que la valeur ne soit transmise par référence (tableau walk peut supprimer les index / éléments en tant que array_filter indirectement via une clause d'utilisation de fonction anonyme en passant le tableau d'origine mais c'est une solution de contournement). Pour conclure ainsi, la modification des valeurs, ni si une valeur est renvoyée ou transmise par référence est moins efficace, mais la marche de tableau fonctionne avec des index et une carte de tableau avec plusieurs tableaux
FantomX1
de plus, peu importe que la marche du tableau prenne le premier paramètre de tableau comme référence, quand on veut le changer, il doit également passer la valeur de l'élément de rappel comme référence
FantomX1
91

L'idée de mapper une fonction à un tableau de données vient de la programmation fonctionnelle. Vous ne devriez pas penser à array_mapune foreachboucle qui appelle une fonction sur chaque élément du tableau (même si c'est comme ça qu'elle est implémentée). Cela devrait être considéré comme l'application indépendante de la fonction à chaque élément du tableau.

En théorie, des choses 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. En effet, an array_mappourrait choisir n'importe quel ordre dans lequel appliquer la fonction aux éléments de (même si en PHP ce n'est pas le cas).

array_walkd'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 lui est $funcnameappliqué, il peut changer l'état global du programme et nécessite donc une seule manière correcte de traiter les éléments.

De retour en PHP, array_mapet array_walksont presque identiques sauf array_walkvous donne plus de contrôle sur l'itération des données et est normalement utilisé pour "changer" les données en place par rapport au retour d'un nouveau tableau "modifié".

array_filterest vraiment une application de array_walk(ou array_reduce) et il est plus ou moins juste fourni pour plus de commodité.

Kendall Hopkins
la source
5
+1 pour votre aperçu du deuxième paragraphe de "En théorie, des choses telles que le mappage de fonctions peuvent être effectuées en parallèle, car la fonction appliquée aux données ne doit effectuer que les données et NON l'état global." Pour nous, programmeurs parallèles, c'est une chose utile à garder à l'esprit.
etherice
Pouvez-vous expliquer comment array_filter()peut être mis en œuvre en utilisant array_walk()?
pfrenssen
40

De la documentation,

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

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

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

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

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

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

Steven Schlansker
la source
Impossible de comprendre pourquoi mes valeurs de tableau ont disparu. En regardant la documentation, j'ai supposé avoir array_walkrenvoyé un tableau similaire array_mapet j'ai pensé que le problème était dans ma fonction. Je n'ai pas réalisé jusqu'à ce que je vois cela que le type de retour est booléen.
Dylan Valade
22

Les autres réponses démontrent assez bien la différence entre array_walk (modification sur place) et array_map (retour de la copie modifiée). Cependant, ils ne mentionnent pas vraiment array_reduce, ce 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-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 faire ce qui suit (je l'ai en fait utilisé comme solution de rechange 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 explique 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 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 voir, les deux fonctions récupèrent l'accumulateur $ et le renvoient à nouveau. Il existe deux différences dans ces fonctions:

  • $ MAP s'ajoutera toujours à $ 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 cela est loin d'être trivial 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 rend ces exemples assez agréables. Cependant, il peut être très inefficace si $ inputs est grand, car le premier appel (carte ou filtre) traversera $ inputs et construira un tableau intermédiaire. Ce tableau intermédiaire est transmis directement dans le deuxième appel, qui traversera à nouveau le tout, puis le tableau intermédiaire devra être récupéré.

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

REMARQUE: Mes implémentations de array_map et array_filter ci-dessus ne se comporteront pas exactement comme PHP, car mon array_map ne peut gérer qu'un seul tableau à la fois et mon array_filter n'utilisera pas "empty" comme fonction $ par défaut. De plus, aucun ne conservera les clés.

Il n'est pas difficile de les faire se comporter comme PHP, mais je sentais que ces complications rendraient l'idée de base plus difficile à repérer.

Warbo
la source
1

La révision suivante cherche à délimiter plus clairement array_filer (), array_map () et array_walk () de PHP, qui proviennent tous de la programmation fonctionnelle:

array_filter () filtre les données, produisant ainsi un nouveau tableau contenant uniquement les éléments souhaités de l'ancien tableau, comme suit:

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

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

code en direct ici

Toutes les valeurs numériques sont filtrées hors du tableau $, 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 d'un rappel à chaque élément, comme suit:

<?php

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

code en direct ici

Dans ce cas, le code applique un rappel à l'aide de strtoupper () intégré, mais une fonction définie par l'utilisateur est également 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, le tableau walk () traverse $ nu et modifie chaque élément par rapport à l'opérateur de référence '&'. Les modifications se produisent sans créer de tableau supplémentaire. La valeur de chaque élément se transforme 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 la démo

Remarque: la fonction de rappel par rapport à array_walk () prend deux paramètres qui acquerront automatiquement la valeur d'un élément et sa clé et dans cet ordre, également lorsqu'elle est invoquée par array_walk (). (Voir plus ici ).

slevy1
la source
1
Notez que les fonctions $lambdaet ne $callbacksont que des extensions eta des fonctions existantes, et sont donc complètement redondantes. Vous pouvez obtenir le même résultat en passant (le nom de) la fonction sous-jacente: $filtered = array_filter($array, 'ctype_alpha');et$nu = array_map('strtoupper', $filtered);
Warbo