Różnica między array_map, array_walk i array_filter

373

Jaka jest dokładnie różnica między array_map, array_walka array_filter. Z dokumentacji wynika, że ​​można przekazać funkcję wywołania zwrotnego, aby wykonać akcję na dostarczonej tablicy. Ale wydaje mi się, że nie znajduję między nimi żadnej szczególnej różnicy.

Czy wykonują to samo?
Czy można ich używać zamiennie?

Byłbym wdzięczny za twoją pomoc z przykładowym przykładem, jeśli w ogóle są one różne.

Gras Double
źródło
To fajna sztuczka do przetwarzania nazwanych tablic za pomocą array_reduce (). Warto przeczytać, jeśli badasz tablicę_mapy, tablicę_sunięcia i filtr_cielki. stackoverflow.com/questions/11563119/…
Lance Cleveland

Odpowiedzi:

564
  • Zmiana wartości:
    • array_mapnie może zmieniać wartości w tablicach wejściowych, a array_walkmoże; w szczególności array_mapnigdy nie zmienia swoich argumentów.
  • Dostęp do klawiszy tablicy:
  • Zwracana wartość:
    • array_mapzwraca nową tablicę, array_walktylko zwraca true. Dlatego też, jeśli nie chcesz, aby utworzyć tablicę w wyniku przechodzenia jednej tablicy, należy użyć array_walk.
  • Iterowanie wielu tablic:
    • array_mapmoże również odbierać dowolną liczbę tablic i może iterować je równolegle, podczas gdy array_walkdziała tylko na jednej.
  • Przekazywanie dowolnych danych do oddzwonienia:
    • array_walkmoże otrzymać dodatkowy dowolny parametr, aby przekazać go do wywołania zwrotnego. Jest to w większości nieistotne od PHP 5.3 (kiedy wprowadzono anonimowe funkcje ).
  • Długość zwracanej tablicy:
    • Powstała tablica array_mapma taką samą długość jak największa tablica wejściowa; array_walknie zwraca tablicy, ale jednocześnie nie może zmienić liczby elementów oryginalnej tablicy; array_filterwybiera tylko podzbiór elementów tablicy zgodnie z funkcją filtrowania. Zachowuje klucze.

Przykład:

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

Wynik:

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
źródło
3
Podręcznik PHP mówi: „array_walk (): potencjalnie można zmienić tylko wartości tablicy;”
feeela
10
„tablica_mapa nie może działać z kluczami tablic” to nie jest prawda:array_map(callback($key, $value), array_keys($array), $array)
Jarek Jakubowski
12
Nadal nie ma dostępu do kluczy żadnej tablicy, uzyskuje dostęp do wartości wprowadzonych do tablicy utworzonej z kluczy. Jest to obejście, nie neguje stwierdzenia.
inarilo
podczas gdy array_map nie zmienia domyślnie wartości, przypisując wynik do tej samej tablicy, zasadniczo ją zmienia, a „paradoksalnie” spacer_tablicy, który działa na tej samej tablicy, nie zmieni swoich wartości bezpośrednio, chyba że zostanie przekazany wartość przez referencję (tablica walk może usuwać indeksy / elementy jako filter_filter pośrednio poprzez anonimową klauzulę use przekazującą oryginalną tablicę, ale jest to obejście). Podsumowując, zmiana wartości, ani jeśli wartość jest zwracana lub przekazywana przez referencję, ma mniejszą efektywność, ale spacer po tablicy działa z indeksami i mapą tablicy z wieloma tablicami
FentomX1
ponadto wygląda na to, że bez względu na to, że spacer po tablicy bierze pierwszy parametr tablicy jako odniesienie, gdy chce się go zmienić, musi przekazać również wartość elementu wywołania zwrotnego jako odniesienie
FentomX1
91

Pomysł mapowania funkcji na tablicę danych pochodzi z programowania funkcjonalnego. Nie powinieneś myśleć o tym array_mapjako o foreachpętli, która wywołuje funkcję na każdym elemencie tablicy (nawet jeśli tak jest zaimplementowana). Należy pomyśleć o zastosowaniu tej funkcji do każdego elementu w tablicy niezależnie.

Teoretycznie takie rzeczy jak mapowanie funkcji można wykonywać równolegle, ponieważ funkcja zastosowana do danych powinna TYLKO wpływać na dane, a NIE na stan globalny. Wynika to z faktu, że a array_mapmoże wybrać dowolną kolejność zastosowania funkcji do elementów w (nawet jeśli w PHP tak nie jest).

array_walkz drugiej strony jest to dokładnie odwrotne podejście do obsługi tablic danych. Zamiast obsługiwać każdy element osobno, używa on state ( &$userdata) i może edytować element w miejscu (podobnie jak w pętli foreach). Ponieważ za każdym razem, gdy element zostanie $funcnamezastosowany, może on zmienić globalny stan programu i dlatego wymaga jednego poprawnego sposobu przetwarzania elementów.

Z powrotem w krainie PHP array_mapi array_walksą prawie identyczne, z wyjątkiem tego, array_walkże dają większą kontrolę nad iteracją danych i zwykle są używane do „zmiany” danych w miejscu w porównaniu do zwracania nowej „zmienionej” tablicy.

array_filterjest tak naprawdę aplikacją array_walk(lub array_reduce) i mniej więcej zapewnia wygodę.

Kendall Hopkins
źródło
5
+1 za wgląd w akapit drugi: „Teoretycznie takie rzeczy jak mapowanie funkcji można wykonać równolegle, ponieważ funkcja zastosowana do danych powinna WYŁĄCZNIE wpłynąć na dane, a NIE na stan globalny”. Dla nas programistów równoległych warto o tym pamiętać.
etherice
Czy możesz wyjaśnić, w jaki sposób array_filter()można go zaimplementować array_walk()?
pfrenssen
40

Z dokumentacji

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

array_walk bierze tablicę i funkcję Fi modyfikuje ją, zastępując każdy element x F(x).

array tablica_mapa (callback $ callback, tablica $ arr1 [, tablica $ ...]) <- zwraca tablicę

array_map robi dokładnie to samo, zróżnicą, że zamiast modyfikować w miejscu, zwraca nową tablicę z transformowanymi elementami.

array array_filter (array $ input [, callback $ callback]) <- zwróć tablicę

array_filter z funkcją F, zamiast przekształcać elementy, usunie wszystkie elementy, dla których F(x)nie jest to prawdą

Steven Schlansker
źródło
Nie mogłem zrozumieć, dlaczego zniknęły moje wartości tablicowe. Patrząc na dokumentację, założyłem, że array_walkzwrócił tablicę podobną do tej array_mapi zorientowałem się, że problem leży w mojej funkcji. Nie zdawałem sobie sprawy, dopóki nie zobaczyłem, że typ zwracany jest wartością logiczną.
Dylan Valade,
22

Pozostałe odpowiedzi dość dobrze pokazują różnicę między array_walk (modyfikacja lokalna) a array_map (zwracana zmodyfikowana kopia). Jednak tak naprawdę nie wspominają o array_reduce, co jest świetnym sposobem na zrozumienie array_map i array_filter.

Funkcja array_reduce pobiera tablicę, funkcję dwóch argumentów i „akumulator”, takie jak to:

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

Elementy tablicy są łączone z akumulatorem pojedynczo, przy użyciu podanej funkcji. Wynik powyższego wywołania jest taki sam, jak w przypadku:

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

Jeśli wolisz myśleć w kategoriach pętli, to jest jak wykonanie następujących czynności (faktycznie wykorzystałem to jako awarię, gdy funkcja array_reduce nie była dostępna):

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

Ta wersja z zapętleniem wyjaśnia, dlaczego nazwałem trzeci argument „akumulatorem”: możemy go używać do gromadzenia wyników podczas każdej iteracji.

Co to ma wspólnego z array_map i array_filter? Okazuje się, że oba są szczególnym rodzajem zmniejszania tablicy. Możemy zaimplementować je w następujący sposób:

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

Zignoruj ​​fakt, że array_map i array_filter przyjmują argumenty w innej kolejności; to tylko kolejne dziwactwo PHP. Ważne jest to, że prawa strona jest identyczna, z wyjątkiem funkcji, które nazwałem $ MAP i $ FILTER. Jak więc wyglądają?

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

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

Jak widać, obie funkcje pobierają akumulator $ i zwracają go ponownie. Istnieją dwie różnice w tych funkcjach:

  • $ MAP zawsze dołącza się do $ accumulator, ale $ FILTER zrobi to tylko wtedy, gdy $ function ($ element) ma wartość TRUE.
  • $ FILTER dołącza oryginalny element, ale $ MAP dołącza funkcję $ (element $).

Zauważ, że jest to dalekie od bezużytecznych ciekawostek; możemy go użyć do zwiększenia wydajności naszych algorytmów!

Często możemy zobaczyć kod podobny do tych dwóch przykładów:

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

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

Użycie array_map i array_filter zamiast pętli sprawia, że ​​te przykłady wyglądają całkiem ładnie. Jednak może być bardzo nieefektywne, jeśli $ input jest duży, ponieważ pierwsze wywołanie (mapa lub filtr) przejdzie $ $ i zbuduje tablicę pośrednią. Ta tablica pośrednia jest przekazywana bezpośrednio do drugiego wywołania, które ponownie przejdzie całą sprawę, a następnie tablica pośrednia będzie musiała zostać wyrzucona.

Możemy pozbyć się tej pośredniej tablicy, wykorzystując fakt, że zarówno array_map, jak i array_filter są przykładami zmniejszenia tablicy. Łącząc je, musimy przejrzeć $ $ tylko raz w każdym przykładzie:

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

UWAGA: Moje implementacje array_map i array_filter powyżej nie będą zachowywać się dokładnie tak, jak PHP, ponieważ moja array_map może obsługiwać tylko jedną tablicę na raz, a mój array_filter nie użyje „pustego” jako domyślnej funkcji $. Ponadto żaden nie zachowa kluczy.

Nie jest trudno sprawić, by zachowywały się jak PHP, ale czułem, że te komplikacje utrudnią dostrzeżenie podstawowej idei.

Warbo
źródło
1

Poniższa wersja ma na celu wyraźniejsze określenie PHP array_filer (), array_map () i array_walk (), z których wszystkie pochodzą z programowania funkcjonalnego:

array_filter () odfiltrowuje dane, w wyniku czego nowa tablica zawiera tylko pożądane elementy poprzedniej tablicy, w następujący sposób:

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

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

kod na żywo tutaj

Wszystkie wartości liczbowe są odfiltrowywane z tablicy $, pozostawiając $ filtrowane tylko z rodzajami owoców.

array_map () tworzy również nową tablicę, ale w przeciwieństwie do array_filter () tablica wynikowa zawiera każdy element wejściowego $ filtrowanego, ale ze zmienionymi wartościami, dzięki zastosowaniu wywołania zwrotnego do każdego elementu, jak następuje:

<?php

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

kod na żywo tutaj

W tym przypadku kod stosuje wywołanie zwrotne za pomocą wbudowanej funkcji strtoupper (), ale funkcja zdefiniowana przez użytkownika jest również inną wykonalną opcją. Wywołanie zwrotne ma zastosowanie do każdego elementu $ filtrowanego, a zatem wywołuje $ nu, którego elementy zawierają wielkie litery.

W następnym fragmencie tablica walk () przechodzi przez $ nu i wprowadza zmiany w każdym elemencie względem operatora odniesienia „&”. Zmiany następują bez tworzenia dodatkowej tablicy. Wartość każdego elementu zamienia się w bardziej informacyjny ciąg określający jego klucz, kategorię i wartość.

<?php

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

Zobacz demo

Uwaga: funkcja zwrotna w odniesieniu do array_walk () przyjmuje dwa parametry, które automatycznie uzyskują wartość elementu i jego klucza oraz w tej kolejności, również w przypadku wywołania przez array_walk (). (Zobacz więcej tutaj ).

slevy1
źródło
1
Zauważ, że funkcje $lambdai $callbacksą tylko rozszerzeniem istniejących funkcji, a zatem są całkowicie zbędne. Możesz uzyskać ten sam wynik, przekazując (nazwę) podstawową funkcję: $filtered = array_filter($array, 'ctype_alpha');i$nu = array_map('strtoupper', $filtered);
Warbo