Unterschied zwischen array_map, array_walk und array_filter

373

Was genau ist das der Unterschied zwischen array_map, array_walkund array_filter. Aus der Dokumentation geht hervor, dass Sie eine Rückruffunktion übergeben können, um eine Aktion für das bereitgestellte Array auszuführen. Aber ich scheine keinen besonderen Unterschied zwischen ihnen zu finden.

Führen sie das Gleiche aus?
Können sie austauschbar verwendet werden?

Ich würde mich über Ihre Hilfe bei einem anschaulichen Beispiel freuen, wenn sie überhaupt unterschiedlich sind.

Gras Double
quelle
Dies ist ein cooler Trick für die Verarbeitung benannter Arrays über array_reduce (). Es lohnt sich zu lesen, wenn Sie array_map, array_walk und array_filter untersuchen. stackoverflow.com/questions/11563119/…
Lance Cleveland

Antworten:

564
  • Werte ändern:
    • array_mapkann die Werte in Eingabearrays nicht ändern, solange dies möglich array_walkist; insbesondere array_mapändert nie seine Argumente.
  • Zugriff auf Array-Schlüssel:
  • Rückgabewert:
    • array_mapGibt ein neues Array zurück, gibt array_walknur zurück true. Wenn Sie also kein Array als Ergebnis des Durchlaufens eines Arrays erstellen möchten, sollten Sie verwenden array_walk.
  • Iterieren mehrerer Arrays:
    • array_mapkann auch eine beliebige Anzahl von Arrays empfangen und diese parallel durchlaufen, während array_walknur eines bearbeitet wird .
  • Übergabe beliebiger Daten an Rückruf:
    • array_walkkann einen zusätzlichen beliebigen Parameter empfangen, der an den Rückruf übergeben wird. Dies ist seit PHP 5.3 (als anonyme Funktionen eingeführt wurden) größtenteils irrelevant .
  • Länge des zurückgegebenen Arrays:
    • Das resultierende Array von array_maphat die gleiche Länge wie das größte Eingabearray. array_walkgibt kein Array zurück, kann aber gleichzeitig die Anzahl der Elemente des ursprünglichen Arrays nicht ändern; array_filterwählt nur eine Teilmenge der Elemente des Arrays gemäß einer Filterfunktion aus. Die Schlüssel bleiben erhalten.

Beispiel:

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

Ergebnis:

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
)
Artefakt
quelle
3
Das PHP-Handbuch sagt: "array_walk (): Nur die Werte des Arrays können möglicherweise geändert werden."
Feeela
10
"array_map kann nicht mit den Array-Schlüsseln arbeiten" Dies ist nicht wahr:array_map(callback($key, $value), array_keys($array), $array)
Jarek Jakubowski
12
Es greift immer noch nicht auf die Schlüssel eines Arrays zu, sondern greift auf die Werte zu, die Sie in ein Array eingegeben haben, das Sie aus den Schlüsseln erstellt haben. Es ist eine Problemumgehung, die die Aussage nicht negiert.
Inarilo
Während array_map Werte nicht implizit ändert, ändert es durch Zuweisen des Ergebnisses zu demselben Array im Grunde genommen, und 'paradoxerweise' ändert array_walk, das mit demselben Array selbst arbeitet, seine Werte nicht direkt, es sei denn, der Wert wird als Referenz (Array übergeben) walk kann Indizes / Elemente als array_filter indirekt über eine anonyme Funktionsverwendungsklausel entfernen, die das ursprüngliche Array übergibt, aber es ist eine Problemumgehung. Zusammenfassend lässt sich sagen, dass das Ändern von Werten oder das Zurückgeben oder
Übergeben
Außerdem sieht es so aus, als ob der Array-Spaziergang den ersten Array-Parameter als Referenz verwendet. Wenn man ihn ändern möchte, muss man auch den Wert des Rückrufelements als Referenz übergeben
FantomX1
91

Die Idee der Zuordnung einer Funktion zu Array von Daten stammen aus funktionalen Programmierung. Sie sollten nicht darüber nachdenken , array_mapwie eine foreachSchleife , die eine Funktion auf jedes Element des Arrays nennt (auch wenn das ist , wie es implementiert). Es sollte so gedacht werden, dass die Funktion unabhängig auf jedes Element im Array angewendet wird.

Theoretisch können Dinge wie die Funktionszuordnung parallel durchgeführt werden, da die auf die Daten angewendete Funktion NUR die Daten und NICHT den globalen Zustand beeinflussen sollte. Dies liegt daran, dass Sie eine array_mapbeliebige Reihenfolge auswählen können, in der die Funktion auf die Elemente in angewendet werden soll (obwohl dies in PHP nicht der Fall ist).

array_walkAuf der anderen Seite ist es der genau entgegengesetzte Ansatz zum Umgang mit Datenfeldern. Anstatt jedes Element separat zu behandeln, wird ein Status ( &$userdata) verwendet und das Element kann an Ort und Stelle bearbeitet werden (ähnlich wie bei einer foreach-Schleife). Da jedes Mal, wenn ein Element auf das Element $funcnameangewendet wird, kann es den globalen Status des Programms ändern und erfordert daher eine einzige korrekte Art der Verarbeitung der Elemente.

Zurück im PHP-Land array_mapund array_walkfast identisch, außer dass array_walkSie mehr Kontrolle über die Iteration von Daten haben und normalerweise verwendet werden, um die Daten an Ort und Stelle zu "ändern", anstatt ein neues "geändertes" Array zurückzugeben.

array_filterist wirklich eine Anwendung von array_walk(oder array_reduce) und es ist mehr oder weniger nur der Einfachheit halber vorgesehen.

Kendall Hopkins
quelle
5
+1 für Ihren zweiten Absatz Einblick in "Theoretisch können Funktionen wie die Funktionszuordnung parallel ausgeführt werden, da die auf die Daten angewendete Funktion NUR die Daten und NICHT den globalen Status beeinflussen sollte." Für uns Parallelprogrammierer ist dies eine nützliche Sache.
Etherice
Können Sie erklären, wie array_filter()mit implementiert werden kann array_walk()?
Pfrenssen
40

Aus der Dokumentation,

bool array_walk (array & $ array, Rückruf $ funcname [, gemischte $ userdata]) <-return bool

array_walk nimmt ein Array und eine Funktion Fund ändert sie, indem jedes Element x durch ersetzt wird F(x).

array array_map (Rückruf $ callback, Array $ arr1 [, Array $ ...]) <- Array zurückgeben

array_map macht genau das Gleiche, außer dass anstelle einer Änderung an Ort und Stelle ein neues Array mit den transformierten Elementen zurückgegeben wird.

array array_filter (array $ input [, callback $ callback]) <- Array zurückgeben

array_filter with function entfernt Fanstelle der Transformation der Elemente alle Elemente, für die dies F(x)nicht der Fall ist

Steven Schlansker
quelle
Konnte nicht herausfinden, warum meine Array-Werte verschwunden sind. Beim Betrachten der Dokumentation nahm ich an, dass array_walkein Array wie zurückgegeben wurde, array_mapund stellte fest, dass das Problem in meiner Funktion lag. Erst als ich das sah, wurde mir klar, dass der Rückgabetyp boolesch ist.
Dylan Valade
22

Die anderen Antworten zeigen den Unterschied zwischen array_walk (direkte Änderung) und array_map (modifizierte Kopie zurückgeben) recht gut. Array_reduce wird jedoch nicht wirklich erwähnt. Dies ist eine aufschlussreiche Methode, um array_map und array_filter zu verstehen.

Die Funktion array_reduce verwendet ein Array, eine Funktion mit zwei Argumenten und einen 'Akkumulator' wie folgt:

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

Die Elemente des Arrays werden unter Verwendung der angegebenen Funktion einzeln mit dem Akkumulator kombiniert. Das Ergebnis des obigen Aufrufs ist dasselbe wie folgt:

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

Wenn Sie lieber in Schleifen denken möchten, gehen Sie wie folgt vor (ich habe dies tatsächlich als Fallback verwendet, als array_reduce nicht verfügbar war):

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

Diese Schleifenversion macht deutlich, warum ich das dritte Argument als "Akkumulator" bezeichnet habe: Wir können es verwenden, um Ergebnisse durch jede Iteration zu akkumulieren.

Was hat das mit array_map und array_filter zu tun? Es stellt sich heraus, dass beide eine bestimmte Art von array_reduce sind. Wir können sie folgendermaßen umsetzen:

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

Ignorieren Sie die Tatsache, dass array_map und array_filter ihre Argumente in einer anderen Reihenfolge verwenden. Das ist nur eine weitere Besonderheit von PHP. Der wichtige Punkt ist, dass die rechte Seite bis auf die Funktionen, die ich $ MAP und $ FILTER genannt habe, identisch ist. Wie sehen sie also aus?

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

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

Wie Sie sehen können, nehmen beide Funktionen den $ -Akkumulator auf und geben ihn erneut zurück. Bei diesen Funktionen gibt es zwei Unterschiede:

  • $ MAP wird immer an $ accumulator angehängt, aber $ FILTER tut dies nur, wenn $ function ($ element) TRUE ist.
  • $ FILTER hängt das ursprüngliche Element an, aber $ MAP hängt die $ -Funktion ($ element) an.

Beachten Sie, dass dies alles andere als nutzlose Kleinigkeiten sind. Wir können es verwenden, um unsere Algorithmen effizienter zu machen!

Wir können oft Code wie diese beiden Beispiele sehen:

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

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

Wenn Sie array_map und array_filter anstelle von Schleifen verwenden, sehen diese Beispiele sehr gut aus. Es kann jedoch sehr ineffizient sein, wenn $ input groß ist, da der erste Aufruf (Map oder Filter) $ input durchläuft und ein Zwischenarray erstellt. Dieses Zwischenarray wird direkt an den zweiten Aufruf übergeben, der das Ganze erneut durchläuft. Dann muss das Zwischenarray mit Müll gesammelt werden.

Wir können dieses Zwischenarray loswerden, indem wir die Tatsache ausnutzen, dass array_map und array_filter beide Beispiele für array_reduce sind. Wenn wir sie kombinieren, müssen wir $ input in jedem Beispiel nur einmal durchlaufen:

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

HINWEIS: Meine Implementierungen von array_map und array_filter oben verhalten sich nicht genau wie die von PHP, da meine array_map jeweils nur ein Array verarbeiten kann und mein array_filter nicht "leer" als Standardfunktion für $ verwendet. Außerdem werden keine Schlüssel beibehalten.

Es ist nicht schwer, sie dazu zu bringen, sich wie PHPs zu verhalten, aber ich hatte das Gefühl, dass diese Komplikationen es schwieriger machen würden, die Kernidee zu erkennen.

Warbo
quelle
1

Mit der folgenden Überarbeitung sollen die Array_filer (), array_map () und array_walk () von PHP klarer abgegrenzt werden, die alle aus der funktionalen Programmierung stammen:

array_filter () filtert Daten heraus und erzeugt als Ergebnis ein neues Array, das nur die gewünschten Elemente des vorherigen Arrays enthält, wie folgt:

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

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

Live-Code hier

Alle numerischen Werte werden aus $ array herausgefiltert, wobei $ nur mit Obstsorten gefiltert wird.

array_map () erstellt auch ein neues Array, aber im Gegensatz zu array_filter () enthält das resultierende Array jedes Element der Eingabe $ gefiltert, jedoch mit geänderten Werten, da auf jedes Element ein Rückruf angewendet wird :

<?php

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

Live-Code hier

Der Code wendet in diesem Fall einen Rückruf mit dem integrierten strtoupper () an, aber eine benutzerdefinierte Funktion ist auch eine weitere praktikable Option. Der Rückruf gilt für jedes Element von $ gefiltert und erzeugt dadurch $ nu, dessen Elemente Großbuchstaben enthalten.

Im nächsten Snippet durchläuft Array walk () $ nu und nimmt Änderungen an jedem Element gegenüber dem Referenzoperator '&' vor. Die Änderungen erfolgen, ohne ein zusätzliches Array zu erstellen. Der Wert jedes Elements ändert sich an Ort und Stelle in eine informativere Zeichenfolge, die seinen Schlüssel, seine Kategorie und seinen Wert angibt.

<?php

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

Siehe Demo

Hinweis: Die Rückruffunktion in Bezug auf array_walk () verwendet zwei Parameter, die automatisch den Wert und den Schlüssel eines Elements und auch in dieser Reihenfolge erfassen, wenn sie von array_walk () aufgerufen werden. (Mehr hier ).

slevy1
quelle
1
Beachten Sie, dass die Funktionen $lambdaund $callbacknur Eta-Erweiterungen vorhandener Funktionen sind und daher vollständig redundant sind. Sie können das gleiche Ergebnis $filtered = array_filter($array, 'ctype_alpha');$nu = array_map('strtoupper', $filtered);
erzielen,