Operații de agregare
O operație de agregare transformă o colecție întreagă într-o singură valoare. Exemple sunt numărul de elemente, suma lor, cel mai mare element sau toate elementele combinate într-un singur rezultat.
PHP ți le oferă pe cele obișnuite ca funcții încorporate. count() numără elementele, min() și max() găsesc cel mai mic și cel mai mare element, iar array_sum() le adună.
Atenție: min() și max() aruncă o excepție ValueError pe un tablou gol („must contain at least one element”), iar programul se oprește dacă nu o prinzi. Verifică întâi count($s) > 0 atunci când tabloul poate fi gol. array_sum([]) este pur și simplu 0.
Pentru combinarea elementelor unul câte unul există array_reduce($items, $callback, $initial). Ea păstrează o valoare curentă, acumulatorul, și apelează funcția ta cu acumulatorul și, pe rând, cu fiecare element. Acumulatorul pornește de la $initial — iar dacă lași $initial deoparte, pornește de la null, nu de la primul element. Această pagină arată ce înseamnă asta și cum obții ambele stiluri de combinare, numite adesea fold și reduce.
Agregări de bază
Cele mai obișnuite agregări sunt numărul de elemente, minimul, maximul, suma și media.
count($numbers)dă numărul de elemente.min($numbers)șimax($numbers)dau cel mai mic și cel mai mare element. Funcționează pentru numere și pentru șiruri de caractere.array_sum($numbers)adună fiecare element. Media este suma împărțită la numărul de elemente. Operatorul/din PHP păstrează partea zecimală atunci când împărțirea nu este exactă, așa că 62 / 4 este 15.5.
Uneori cel mai mic sau cel mai mare element trebuie ales după o regulă, nu după valoarea însăși. min() și max() nu primesc nicio regulă, dar array_reduce() poate păstra elementul mai bun: la fiecare pas compară cel mai bun element de până acum cu cel curent și păstrează câștigătorul.
Uneori fiecare element trebuie convertit întâi cu o funcție, iar rezultatele adunate. Înlănțuie două funcții încorporate: array_map() convertește, array_sum() adună.
Numărul de elemente, minim, maxim, sumă și medie
Pentru numerele 6, 42, 10 și 4 afișează numărul de elemente cu count(), cel mai mare și cel mai mic element cu max() și min(), iar suma cu array_sum(). Media este 62 / 4 = 15.5.
Ieșire: script PHP
<?php
$numbers = [6, 42, 10, 4];
$sum = array_sum($numbers);
echo "Număr de elemente: ", count($numbers), "\n";
echo "Maxim: ", max($numbers), "\n";
echo "Minim: ", min($numbers), "\n";
echo "Medie: ", $sum / count($numbers), "\n";
echo "Sumă: ", $sum, "\n";
Minim și maxim după o regulă
Primul array_reduce() păstrează elementul cu restul mai mic la % 3. Resturile numerelor 5, 42, 10 și 4 sunt 2, 0, 1 și 1, așa că 42 câștigă. Al doilea păstrează șirul de caractere mai lung și găsește "three". Nu se dă nicio valoare inițială, deci $best pornește de la null, iar verificarea $best === null lasă primul element să câștige prima rundă. La egalitate rămâne elementul dinainte.
Ieșire: script PHP
<?php
$numbers = [5, 42, 10, 4];
$words = ["one", "two", "three", "four"];
// Elementul al cărui rest la împărțirea cu 3 este cel mai mic.
// $best pornește de la null, așa că primul element câștigă mereu prima rundă.
$minRem = array_reduce(
$numbers,
fn($best, $n) => ($best === null || $n % 3 < $best % 3) ? $n : $best
);
echo $minRem, "\n";
// Cel mai lung cuvânt, comparat după lungime.
$longest = array_reduce(
$words,
fn($best, $w) => ($best === null || strlen($w) > strlen($best)) ? $w : $best
);
echo $longest, "\n";
Suma unei funcții de fiecare element
array_map() convertește fiecare element, array_sum() adună rezultatele. Dublarea numerelor 5, 42, 10 și 4 dă 122. Înjumătățirea lor dă 30.5.
Ieșire: script PHP
<?php
$numbers = [5, 42, 10, 4];
// Convertește întâi fiecare element, apoi adună rezultatele.
echo array_sum(array_map(fn($n) => $n * 2, $numbers)), "\n";
echo array_sum(array_map(fn($n) => $n / 2, $numbers)), "\n";
Fold și Reduce
Fold și reduce combină toate elementele într-o singură valoare, pas cu pas. Ele păstrează o valoare curentă numită acumulator. Pentru fiecare element apelează o funcție cu acumulatorul și elementul, iar funcția returnează noul acumulator.
Diferența este valoarea de pornire:
- Un fold pornește de la o valoare inițială pe care i-o dai tu. Funcția rulează o dată pentru fiecare element. În PHP asta este exact
array_reduce($items, $f, $initial). - Un reduce pornește de la primul element. Funcția rulează începând cu al doilea element, așa că primul element nu trece niciodată prin ea.
În ciuda numelui, array_reduce() este întotdeauna un fold. Fără al treilea argument pornește de la null, iar în aritmetică null contează ca 0 — deci funcția rulează totuși pentru fiecare element. Un reduce adevărat ia primul element ca pornire și pliază restul: array_reduce(array_slice($items, 1), $f, $items[0]). O mică funcție ajutătoare reduce() împachetează linia aceasta.
Asta contează atunci când funcția modifică elementul. Dacă dublează fiecare element, un reduce lasă primul element așa cum este.
PHP nu are un reduce dinspre dreapta. Pentru a plia dinspre dreapta, dă-i lui array_reduce() tabloul inversat de array_reverse(): elementele sosesc de la ultimul la primul, iar funcția primește tot acumulatorul primul și elementul al doilea.
Reduce
Funcția ajutătoare reduce() ia 5 ca pornire și pliază în el 2, 10 și 4. Rezultatul este 21.
Ieșire: script PHP
<?php
// reduce pornește de la primul element și pliază restul în el.
function reduce(array $items, callable $f): mixed {
return array_reduce(array_slice($items, 1), $f, $items[0]);
}
$numbers = [5, 2, 10, 4];
$sum = reduce($numbers, fn($sum, $element) => $sum + $element);
echo $sum, "\n";
Fold
array_reduce() cu valoarea inițială 0 pornește de la 0 și adaugă fiecare element înmulțit cu 2. Fiecare element este dublat: 10 + 4 + 20 + 8 = 42.
Ieșire: script PHP
<?php
$numbers = [5, 2, 10, 4];
// Al treilea argument este valoarea inițială: fiecare element este dublat.
$sumDoubled = array_reduce($numbers, fn($sum, $element) => $sum + $element * 2, 0);
echo $sumDoubled, "\n";
Fold față de Reduce
Ambele apeluri folosesc aceeași funcție, acumulatorul plus elementul înmulțit cu 2. reduce() dă 37, pentru că primul element, 5, este pornirea și nu este dublat. array_reduce() cu valoarea inițială 0 dă 42. Dacă ai lăsa 0 deoparte, ar da tot 42: pornirea ar fi null, iar null + 10 este 10.
Ieșire: script PHP
<?php
// reduce folosește primul element ca pornire, așa că $f nu se aplică niciodată pe el.
function reduce(array $items, callable $f): mixed {
return array_reduce(array_slice($items, 1), $f, $items[0]);
}
$addDoubled = fn($sum, $element) => $sum + $element * 2;
$numbers = [5, 2, 10, 4];
// Aici 5 este valoarea de pornire, deci nu este dublat: 5 + 4 + 20 + 8
echo "Reduce: ", reduce($numbers, $addDoubled), "\n";
// Fiecare element este dublat: 10 + 4 + 20 + 8
echo "Fold: ", array_reduce($numbers, $addDoubled, 0), "\n";
Fold dinspre dreapta
array_reverse($numbers) dă elementele lui array_reduce() de la ultimul până la primul. Adunarea nu ține cont de ordine, așa că rezultatul este din nou 42.
Ieșire: script PHP
<?php
$numbers = [5, 2, 10, 4];
// array_reverse() dă elementele mai departe de la ultimul până la primul.
$sumDoubledRight = array_reduce(
array_reverse($numbers),
fn($sum, $element) => $sum + $element * 2,
0
);
echo $sumDoubledRight, "\n";
Fold cu index
Uneori contează poziția unui element, nu doar valoarea lui.
Funcția de apel a lui array_reduce() nu vede niciodată cheile, dar o buclă foreach simplă le vede. foreach ($numbers as $idx => $element) parcurge de la început. array_reverse($numbers, true) inversează tabloul, dar păstrează cheile originale, pentru că al doilea argument este true — așa că un foreach peste el parcurge de la sfârșit și vede totuși indexul real al fiecărui element. În buclă, un if verifică indexul și decide dacă elementul se adună.
Fold cu index
Se adună numai elementele de pe indexurile pare: indexul 0 ține 5 și indexul 2 ține 10, deci suma este 15.
Ieșire: script PHP
<?php
$numbers = [5, 2, 10, 4];
// foreach dă și indexul, așa că condiția îl poate folosi.
$sumEven = 0;
foreach ($numbers as $idx => $element) {
if ($idx % 2 === 0) {
$sumEven += $element;
}
}
echo $sumEven, "\n";
Fold cu index dinspre dreapta
Aceeași regulă, dar bucla pornește de la indexul 3 și coboară până la 0. Rezultatul este tot 15.
Ieșire: script PHP
<?php
$numbers = [5, 2, 10, 4];
// array_reverse(..., true) păstrează cheile originale: 3, 2, 1, 0.
$sumEven = 0;
foreach (array_reverse($numbers, true) as $idx => $element) {
if ($idx % 2 === 0) {
$sumEven += $element;
}
}
echo $sumEven, "\n";
Reduce în siguranță
Un reduce are nevoie de un prim element de la care să pornească. Pe un tablou gol, funcția ajutătoare reduce() ar citi $items[0], care nu există: PHP afișează un avertisment („Undefined array key 0”) și continuă cu null.
Modul PHP de a spune „nu există rezultat” este null. O mică funcție ajutătoare verifică întâi tabloul: când este gol, returnează null fără să atingă $items[0]. Apelantul verifică apoi dacă este null, exact așa cum $map[$key] ?? $default tratează o cheie lipsă. array_reduce() se comportă deja așa: pe un tablou gol returnează valoarea sa inițială, care este null dacă nu dai niciuna.
Reduce sau null
reduceOrNull() returnează 21 pentru numerele 5, 2, 10 și 4, și null pentru un tablou gol. Ultima linie arată că array_reduce() fără valoare inițială returnează și el null pentru un tablou gol. json_encode() afișează null ca fiind cuvântul null.
Ieșire: script PHP
<?php
// reduceOrNull returnează null în loc să citească un $items[0] lipsă.
function reduceOrNull(array $items, callable $f): mixed {
if ($items === []) {
return null;
}
return array_reduce(array_slice($items, 1), $f, $items[0]);
}
$add = fn($sum, $element) => $sum + $element;
$numbers = [5, 2, 10, 4];
$empty = [];
echo json_encode(reduceOrNull($numbers, $add)), "\n";
echo json_encode(reduceOrNull($empty, $add)), "\n";
// array_reduce() pe un tablou gol returnează valoarea sa inițială: aici null.
echo json_encode(array_reduce($empty, $add)), "\n";
Fold și Reduce cu valori intermediare
Un fold sau un reduce cu valori intermediare nu aruncă pașii dintre. Salvează acumulatorul după fiecare element, așa că rezultatul este un tablou cu toate valorile intermediare.
PHP nu are o funcție încorporată pentru asta. Păstrează o variabilă sumă și adaug-o la un tablou de rezultate cu $result[] = $sum într-o buclă foreach. Pentru un reduce cu valori intermediare, rezultatul începe cu primul element. Pentru un fold cu valori intermediare, rezultatul începe cu valoarea inițială și are un element mai mult decât intrarea.
Reduce cu valori intermediare
Numerele de la 0 la 5 sunt adunate unul câte unul. Tabloul de rezultate ține fiecare sumă de pe drum: 0, 1, 3, 6, 10, 15.
Ieșire: script PHP
<?php
$numbers = [0, 1, 2, 3, 4, 5];
// Primul element este pornirea; fiecare pas următor este salvat în rezultat.
$sum = $numbers[0];
$runningReduceSum = [$sum];
foreach (array_slice($numbers, 1) as $item) {
$sum += $item;
$runningReduceSum[] = $sum;
}
echo implode(", ", $runningReduceSum), "\n";
Fold cu valori intermediare
Același lucru cu valoarea inițială 10. Rezultatul începe chiar cu 10 și are un element mai mult decât intrarea: 10, 10, 11, 13, 16, 20, 25.
Ieșire: script PHP
<?php
$numbers = [0, 1, 2, 3, 4, 5];
// Valoarea inițială 10 este primul element al rezultatului.
$sum = 10;
$runningFoldSum = [$sum];
foreach ($numbers as $item) {
$sum += $item;
$runningFoldSum[] = $sum;
}
echo implode(", ", $runningFoldSum), "\n";