Search code examples
phpjsonassociative-array

PHP - Get latest entry for each element ID in assoc array


I have some json data which I've decoded into an assoc array. This array contains a set of names, their respective ids, some relevant data and year. Here is a sample of my data:

Name ID Year
Gary 1 2016
Miller 2 2018
Spike 3 2019
Miller 2 2020
Gary 1 2018
Miller 2 2019
Gary 1 2017
Spike 3 2020

I have sorted this in descending order of year, but I would also like to retrieve all 3 IDs by their latest entry only. Below is my expected output:

Name ID Year
Miller 2 2020
Spike 3 2020
Gary 1 2018

I am not clear on how to get the latest entry from each respective ID.


Solution

  • One of the easier ways to do this is to loop through the sorted array and extract the record with the first instance of each ID into another array, keyed by the ID so we know when we have already found that ID.

    <?php
    
    $data = [
        ['Name' => 'Gary',      'ID' => 1, 'Year' => 2016],
        ['Name' => 'Miller',    'ID' => 2, 'Year' => 2018],
        ['Name' => 'Spike',     'ID' => 3, 'Year' => 2019],
        ['Name' => 'Miller',    'ID' => 2, 'Year' => 2020],
        ['Name' => 'Gary',      'ID' => 1, 'Year' => 2018],
        ['Name' => 'Miller',    'ID' => 2, 'Year' => 2019],
        ['Name' => 'Gary',      'ID' => 1, 'Year' => 2017],
        ['Name' => 'Spike ',    'ID' => 3, 'Year' => 2020]
    ];
    
    // Sort all records by year in descending order
    usort($data, function ($a, $b)
    {
        return $b['Year'] <=> $a['Year'];
    });
    
    // Create a temporary array for our unique entries. We will use the IDs as the keys
    $latestEntryBuffer = [];
    
    // Loop through the sorted data
    foreach ($data as $currRow)
    {
        /*
            Set a var for the ID, less typing, less chance for error,
            our IDE will let us know if we mistype it somewhere
        */
        $currID = $currRow['ID'];
        
        /*
            If this ID does not have a record in the buffer yet, set
            the current record in the buffer. Since we sorted all data
            by year descending, the first instance of the ID we encounter
            will be the most recent
         */
        if (!array_key_exists($currID, $latestEntryBuffer))
        {
            $latestEntryBuffer[$currID] = $currRow;
        }
    }
    
    // Put all of the rows from the buffer into a simple array
    $lastestEntries = array_values($latestEntryBuffer);
    
    print_r($lastestEntries);
    echo PHP_EOL;
    

    Output:

        Array
        (
            [0] => Array
                (
                    [Name] => Miller
                    [ID] => 2
                    [Year] => 2020
                )
    
            [1] => Array
                (
                    [Name] => Spike
                    [ID] => 3
                    [Year] => 2020
                )
    
            [2] => Array
                (
                    [Name] => Gary
                    [ID] => 1
                    [Year] => 2018
                )
    
        )
    

    Alternatively you could sort by year ascending and blindly assign each record to the buffer by ID, the last record for each ID would be the most recent.