Tym razem temat moim zdaniem wdzięczny i ciekawy, choć może odrobinę problematyczny. Nauczymy się bowiem, czym jest tzw. callback oraz co to są funkcje walk, map, filter oraz reduce.

Zaczynajmy.

Array_walk – wywołaj funkcję na każdym elemencie tablicy

Funkcja array_walk nie zwraca absolutnie niczego i to ją wyróżnia od innych funkcji, które sobie omówimy później.

Przyjmuje ona natomiast dwa argumenty, jednym jest tablica, drugim funkcja, którą chcemy wywołać na każdym elemencie tablicy.

Można zatem powiedzieć, że niewiele się różni od pętli foreach, która również nic nie zwraca, ale na każdym elemencie tablicy wykonuje jakąś akcję.

<?php
$elements = ['a', 'b', 'c'];

array_walk($elements, function ($value, $key) {
  echo "{$key} => {$value}\n";
});
//0 => a 1 => b 2 => c

Tutaj mamy array_walk, które przechodzi po tablicy elements. Na każdym jej elemencie wykonuje funkcję, która przyjmuje wartość oraz klucz/indeks i wykonuje dowolnie przez nas zdefiniowaną akcję.

Tutaj mamy wypisanie klucza(indeksu) oraz wartości oraz znak nowej linii \n (przykład jest z internetu, z dokumentacji PHP).

Jako że pracujemy z plikami, które otwieramy w przeglądarce, możemy zamienić sobie to \n na tag nowej linii <\br>.

Możemy to zrobić albo ręcznie, albo poprzez funkcję nl2br (newline to breakline):

<?php
$elements = ['a', 'b', 'c'];

array_walk($elements, function ($value, $key) {
  echo nl2br("{$key} => {$value}\n");
});
// 0 => a
// 1 => b
// 2 => c

Nawiasy klamrowe {} wokół interpolowanych zmiennych też są opcjonalne. Ta funkcja wyświetla indeks danego elementu oraz jego zawartość. Wydaje się mało użyteczna.

Możemy na przykład zrobić coś innego – darować sobie wypisywanie indeksu (zmienna $key). Natomiast wartość możemy pokazać wraz z liczbą jej odpowiadającą.

Jest taka funkcja jak ord(). Można do niej „wrzucić” literę, zaś ona „wypluje” liczbę, jaka tej literze odpowiada (każda litera to dla komputera jakaś liczba).

Spróbujmy:

<?php
$elements = ['a', 'b', 'c'];

array_walk($elements, function ($letter) {
  echo "letter $letter => " . ord($letter) . "</br>";
});
// letter a => 97
// letter b => 98
// letter c => 99

Możemy też te litery pokazać w liczbach binarnych (tak je „widzi” komputer)

<?php
$elements = ['a', 'b', 'c'];

array_walk($elements, function ($letter) {
    $letter_dec = ord($letter);
    $letter_bin = decbin($letter_dec);

  echo "letter $letter => $letter_dec($letter_bin in binary)</br>";
});
// letter a => 97(1100001 in binary)
// letter b => 98(1100010 in binary)
// letter c => 99(1100011 in binary)

Tak korzystamy z array_walk. Funkcja nie zwraca niczego.

Przyjmuje tablicę, po której ma przejść oraz tzw. callback, czyli naszą funkcję jako argument.

Ten nasz callback przyjmuje wartość, element listy oraz opcjonalnie jako drugi argument -klucz/indeks.

Przechodzimy po każdym elemencie tablicy (stąd pewnie nazwa „walk”) i wywołujemy na nim nasz callback, według własnego uznania.

Array_map – zwraca nową tablicę

Funkcja array_map jest bardzo podobna do array_walk, z tą różnicą, że array_map zwraca nową tablicę, której elementy, to elementy z pierwszej na których wykonano callback.

Krótko mówiąc, moglibyśmy wyświetlić sobie nasze literki zapisane wielką literą, używając array_walk:

<?php
$elements = ['a', 'b', 'c'];

array_walk($elements, function ($letter) {
    echo ucfirst($letter) . "</br>";
});
// A
// B
// C

Jeżeli jednak chcemy otrzymać nową tablicę, której elementy to te literki z elements, ale wielką literą – użyjemy array_map:

<?php
$elements = ['a', 'b', 'c'];
$elements_upper = array_map(function($el){
    return ucfirst($el);
}, $elements);
print_r($elements_upper);
//Array ( [0] => A [1] => B [2] => C )

Z jakichś względów tutaj najpierw piszemy funkcję callback, później zaś podajemy nazwę tablicy. Dziwne.

Funkcję możemy też podać na różne sposoby. Nie musi to być funkcja anonimowa, wtedy podajemy jej nazwę jako napis:

<?php
$elements = ['a', 'b', 'c'];
$elements_upper = array_map('ucfirst', $elements);
print_r($elements_upper);
//Array ( [0] => A [1] => B [2] => C )

Tutaj podaliśmy nazwę wbudowanej funkcji ucfirst, do której każdy element tablicy $elements zostanie podany i taka nowa tablica zostanie zwrócona, z elementami poddanymi działaniu ucfirst.

Możemy też w ten sposób podawać własne funkcje:

$elements = [1,2,3];
function square($num){
    return $num * $num;
}
$squared = array_map('square', $elements);
print_r($squared);
//Array ( [0] => 1 [1] => 4 [2] => 9 )

Stworzyliśmy własną funkcję square, która podnosi do kwadratu daną liczbę. Stworzyliśmy nową tablicę z elementów $elements, poddanych działaniu funkcji square – i mamy tam kwadraty liczb z elements.

Oczywiście takie tworzenie funkcji dla jednorazowego użycia nie jest wygodne. Dlatego albo korzystamy z funkcji anonimowych (bez nazwy):

$elements = [1,2,3];
$squared = array_map(function($num){
    return $num * $num;
}, $elements);
print_r($squared);
//Array ( [0] => 1 [1] => 4 [2] => 9 )

Albo, jeżeli nasza funkcja wykonuje tylko jedną, prostą operację – korzystamy z funkcji strzałkowych, które wyglądają tak:

<?php
$elements = [1,2,3];
$squared = array_map(fn($num) => $num * $num, $elements);
print_r($squared);
//Array ( [0] => 1 [1] => 4 [2] => 9 )

Przejdźmy sobie teraz przez array_walk po nowo utworzonej tablicy squared:

<?php
$elements = [1,2,3];
$squared = array_map(fn($num) => $num * $num, $elements);
array_walk($squared, function($num){
    echo "$num</br>";
});
// 1
// 4
// 9

Map zwraca nową tablicę z każdym elementem poddanym działaniu jakiejś funkcji. Walk przechodzi po tablicy, każdy element poddając działaniu jakiejś funkcji (poniekąd jak pętla foreach).

Array_filter – odfiltruj elementy, które zwracają fałsz

Mamy taką oto tablicę:

$elements = ['a', 'b', 'c', 1, 2, 3, 'ab', 'bc'];

Chcemy otrzymać kopię tej tablicy, zawierającą tylko te elementy, które po pierwsze są typu napisowego, po drugie ich długość wynosi 1.

Najpierw odsiejmy te, które napisami nie są, przy użyciu nowo poznanej funkcji array_filter:

$strings_only = array_filter($elements, function($el){
    return is_string($el);
});
print_r($strings_only);
//Array ( [0] => a [1] => b [2] => c [6] => ab [7] => bc )

Nasza funkcja przechodzi po tablicy elements i tworzy nową tablicę, dla której jest postawiony warunek: element musi być stringiem, wrzucony w is_string musi zwrócić prawdę.

Teraz operatorem logicznym „&&”, którego jeszcze chyba nie poznaliśmy, dodamy sobie drugi warunek: element musi mieć długość 1, nie więcej, nie mniej:

<?php
$elements = ['a', 'b', 'c', 1, 2, 3, 'ab', 'bc'];
$letters_only = array_filter($elements, function($el){
    return is_string($el) && strlen($el) === 1;
});
print_r($letters_only);
//Array ( [0] => a [1] => b [2] => c )

Teraz mamy tylko te elementy, które są literami, czyli są napisem i ich długość to 1. Funkcja strlen sprawdza długość napisu, zaś operator „&&” to logiczne „i”, które sprawia, że oba warunki muszą być prawdziwe.

W zasadzie moglibyśmy dodawać kolejne warunki, to już jednak sobie darujmy. Warto zauważyć, że w PHP możemy użyć albo operatora „&&” albo „and”, oba znaczą to samo.

Array_reduce – redukcja tablicy do jednej wartości

Funkcja array_walk przechodzi po elementach tablicy i wywołuje na nich zadaną funkcję, niczego nie zwracając.

<?php
$elements = ['a', 'b', 'c'];
array_walk($elements, function($letter){
    echo ucfirst($letter) . "</br>";
});
// A
// B
// C

Funkcja array_map zwraca nową tablicę, której elementy zostały poddane działaniu danej funkcji.

<?php
$elements = [1,2,3];
$squared = array_map(fn($num) => $num * $num, $elements);
print_r($squared);
// Array ( [0] => 1 [1] => 4 [2] => 9 )

Funkcja array_filter zwraca tablicę, której elementy zostały odfiltrowane z innej tablicy, według logiki danej funkcji:

<?php
$elements = [0, 1,2, -1, 3, -3];
$above_zero = array_filter($elements, function($el){
    return $el > 0;
});
print_r($above_zero);
// Array ( [1] => 1 [2] => 2 [4] => 3 )

Funkcja array_reduce jest chyba najciekawsza, ponieważ dokonuje ona konwersji a w zasadzie redukcji z typu tablica na typ prosty.

Aby to zrobić potrzebuje ona funkcji, która przyjmuje akumulator (obecny licznik) oraz element i zwraca nową wartość akumulatora.

Brzmi groźnie, więc sobie to wytłumaczmy na przykładzie:

<?php
function sum($acc, $el){
    return $acc + $el;
}
$arr = [1,2,3,4,5];
$sum_of_arr = array_reduce($arr, 'sum', 0);
echo $sum_of_arr;
//15

Funkcja sum przyjmuje akumulator i element, zwraca akumulator plus element. W zmiennej arr mamy takie sobie oto liczby od 1 do 5.

Funkcja array_reduce na każdym elemencie arr wykonuje sum z akumulatorem na samym początku ustawionym na 0.

I tak do tego 0 dodawane jest 1,2,3,4,5 dając ostatecznie 15.

Taki jest wynik redukcji naszej tablicy do typu prostego.

Zróbmy sobie inny przykład: mamy tablicę liter, które chcemy zamienić na liczby (służy do tego funkcja ord) i dodać ich wartość liczbową do siebie:

<?php
$letters = ['a', 'b', 'c', 'd'];
function reduce_letters($acc, $letter){
    return $acc + ord($letter);
}
$letters_reduced = array_reduce($letters, 'reduce_letters', 0);
echo $letters_reduced;
//394

Mamy nasze litery w tablicy letters.

Mamy funkcję reduce_letters, która przyjmuje akumulator i literę. Do akumulatora dodaje wartość liczbową litery pozyskaną z funkcji ord (zamienia literę na liczbę).

Mamy zmienną letters_reduced, gdzie wywołujemy na tablicy funkcję array_reduce, podając naszą funkcję i początkową wartość akumulatora ustawioną na 0.

Litery zamienione na ich reprezentację liczbową i dodane do siebie, tablica zredukowana do typu prostego liczbowego według naszej logiki.

Wbudowane funkcje redukujące – sum, product

Warto jeszcze wspomnieć, że niektóre typy redukcji tablicy do typu prostego są wbudowane w PHP.

Jeżeli chcemy tablicę (listę) liczb dodać do siebie, mamy funkcję array_sum:

<?php
echo array_sum([1,2,3,4,5]);
//15

Jeżeli chcemy liczby znajdujące się w tablicy wymnożyć przez siebie, mamy od tego funkcję array_product:

<?php
echo array_product([1,2,3,4,5]);
//120

Myślę, że w dzisiejszej lekcji poznaliśmy naprawdę dużo i pora zakończyć ten odcinek.

Jeżeli coś jest jeszcze niejasne, warto to sobie na spokojnie przeanalizować.