phparraysmultidimensional-arraygroupingmerging-data

Merge rows of two 2d arrays, group on a column, and append non-null values in another column within each group


I am refactoring a calendar app. The calendar has a user-defined grid (slots where bookings can occur), but needs to display "off-the-grid" bookings as well. Think of "regular" slots at 08:00, 09:00 and "irregular" slots when someone is booked at 08:39. For business reasons, I need to display these slots differently (CSS), otherwise they behave the same. I've searched the PHP Manual, but the built-in array functions don't do exactly what I need.

$officialGrid = [
    ['grid' => TRUE, 'time' => '08:00', 'content' => NULL],
    ['grid' => TRUE, 'time' => '09:00', 'content' => NULL],
];

$bookings = [
    ['grid' => NULL, 'time' => '08:00', 'content' => 'Paul Simon'],
    ['grid' => NULL, 'time' => '08:00', 'content' => 'Art Garfunkel'],
    ['grid' => NULL, 'time' => '08:39', 'content' => 'Homer J. Simpson'],
];

I could just append these arrays, but for performance reasons would like shorten the result to:

$timeSlotsToDisplay = [
    ['grid' => TRUE, 'time' => '08:00', 'content' => 'Paul Simon, Art Garfunkel'], //regular
    ['grid' => NULL, 'time' => '08:39', 'content' => 'Homer J. Simpson'], //irregular
    ['grid' => TRUE, 'time' => '09:00', 'content' => NULL], //vacant
];

I'm also flexible to change data types for the values (content might be an array). Is there any elegant solution for merging these arrays, other than start looping and comparing?

PS: Just to illustrate, in PostgreSQL terms, I need to SELECT DISTINCT ON (time) grid, time, string_agg(content) FROM myDB GROUP BY time ORDER BY time, grid; (please ignore possible keywords, not quoting due to formatting, also haven't tested the query).


Solution

  • I see nothing wrong with the loops.. but I suggest another structure for the $officialGrid array

    $officialGrid = array(
        '08:00' => array('grid' => TRUE, 'content' => NULL),
        '09:00' => array('grid' => TRUE, 'content' => NULL));
    
    $bookings = array(
        array('grid' => NULL, 'time' => '08:00', 'content' => 'Paul Simon'),
        array('grid' => NULL, 'time' => '08:00', 'content' => 'Art Garfunkel'),
        array('grid' => NULL, 'time' => '08:39', 'content' => 'Homer J. Simpson'));
    
    $timeSlotsToDisplay = $officialGrid;
    
    array_map(function($el) use(&$timeSlotsToDisplay, $officialGrid) {
       $timeSlotsToDisplay[$el['time']] = array(
         'content' => 
           (isset($timeSlotsToDisplay[$el['time']]) ?
            trim($timeSlotsToDisplay[$el['time']]['content'], ',') . ',' : '') .
           $el['content'],
         'grid' => isset($officialGrid[$el['time']]) ? true : null
       );
    }, $bookings);
    
    ksort($timeSlotsToDisplay);
    var_dump($timeSlotsToDisplay);
    

    array_map can be replaced by a single foreach loop.