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