Umiarkowanie proste ćwiczenie – odtwarzamy w PHP znaną z JavaScript funkcję groupBy w jej podstawowym działaniu.
W PHP mamy świetną funkcję array_column, natomiast w JS – groupBy. Służy ona do grupowania tablic zawierających różnego rodzaju tablice asocjacyjne po odpowiednim polu.
W skrócie, mamy poniższą listę uczniów/osób:
<?php
$records = array(
array(
'name' => 'John',
'age' => 20,
'grade' => 5
),
array(
'name' => 'Sally',
'age' => 20,
'grade' => 4
),
array(
'name' => 'Jane',
'age' => 30,
'grade' => 5
),
array(
'name' => 'Peter',
'age' => 30,
'grade' => 4
)
);
I chcemy ją pogrupować, np. po wieku, albo po ocenie.
Piszemy naszą funkcję:
function group_by($items, $key){
$group = array();
$types = array_column($items, $key);
foreach($types as $type) {
$group[$type] = array();
}
return $group;
}
Tutaj tworzymy nową tablicę $group. Następnie pobieramy wszystkie wartości o danym kluczu i tworzymy takie klucze w naszym obiekcie. To już połowa sukcesu, zobaczmy co otrzymamy:
$byAge = group_by($records, 'age');
echo "<pre>";
print_r($byAge);
echo "</pre>";
// Array
// (
// [20] => Array
// (
// )
// [30] => Array
// (
// )
// )
Aha, czyli stworzyło nam klucze 20 i 30 (takie są wartości dla „age” naszych obiektów) i podstawiło pod nie puste obiekty.
Dla grade zadziała?
$byAge = group_by($records, 'grade');
echo "<pre>";
print_r($byAge);
echo "</pre>";
// Array
// (
// [5] => Array
// (
// )
// [4] => Array
// (
// )
// )
Jak widać mamy tylko piątki i czwórki i tak będzie nam grupował, po ocenie. Problem w tym, że te tablice są puste, wypadałoby je wypełnić.
Druga pętla foreach:
function group_by($items, $key){
$group = array();
$types = array_column($items, $key);
foreach($types as $type) {
$group[$type] = array();
}
foreach($items as $item) {
$itemKey = $item[$key];
$group[$itemKey] = $item;
}
return $group;
}
Wygląda nieźle, ale zawiera pewien błąd.
$byAge = group_by($records, 'grade');
echo "<pre>";
print_r($byAge);
echo "</pre>";
// Array
// (
// [5] => Array
// (
// [name] => Jane
// [age] => 30
// [grade] => 5
// )
// [4] => Array
// (
// [name] => Peter
// [age] => 30
// [grade] => 4
// )
// )
Mieliśmy dwóch uczniów piątkowych i dwóch czwórkowych i pod tymi kluczami mieliśmy mieć ich listę, zamiast tego dostaliśmy po jednym uczniu.
Czy wiemy, gdzie znajduje się błąd?
Podpowiem, tutaj:
foreach($items as $item) {
$itemKey = $item[$key];
$group[$itemKey] = $item;
}
Tutaj nadpisujemy wartość, zamiast dodać pod danym kluczem do jego tablicy nowy obiekt.
Wersja działająca:
function group_by($items, $key){
$group = array();
$types = array_column($items, $key);
foreach($types as $type) {
$group[$type] = array();
}
foreach($items as $item) {
$itemKey = $item[$key];
$group[$itemKey][] = $item;
}
return $group;
}
Teraz pod każdym z unikalnych wartości podanego klucza dodajemy obiekt do tablicy. Po ludzku – otrzymujemy coś takiego:
$byAge = group_by($records, 'grade');
echo "<pre>";
print_r($byAge);
echo "</pre>";
// Array
// (
// [5] => Array
// (
// [0] => Array
// (
// [name] => John
// [age] => 20
// [grade] => 5
// )
// [1] => Array
// (
// [name] => Jane
// [age] => 30
// [grade] => 5
// )
// )
// [4] => Array
// (
// [0] => Array
// (
// [name] => Sally
// [age] => 20
// [grade] => 4
// )
// [1] => Array
// (
// [name] => Peter
// [age] => 30
// [grade] => 4
// )
// )
// )
Możemy oczywiście pogrupować po wieku, albo po imieniu. Po prostu – odtworzyliśmy działanie funkcji groupBy z języka JavaScript w PHP.
Możemy to sobie na spokojnie przeanalizować, ale tak to działa.
Oczywiście możemy znaleźć inne sposoby, aby to osiągnąć, a nawet usprawnić tę funkcję, aby mogła robić bardziej skomplikowane grupowanie i w pełni odpowiadała działaniu groupBy z JS (możliwość przyjęcia funkcji zamiast klucza).