1

I have a messaging system in my app and I'm pulling my hair out trying to figure out the best way to give the user a menu to select which conversation they want to read. I'm trying to mirror the GroupMe style menu where on the left are your conversations. This conversation list is dynamic so the most recent conversations are at the top and update while you are using the app.

I'm using Laravel 7 and Firebase

Here's the data from my backend (prettified)

Array
(
    [-MC1kTfrrDHY3ZbidJ4Q] => Array
        (
            [from] => lSUfZ4sgEJd
            [message] => test message four no more!
            [startedAt] => 2020-07-12 11:21:10
            [to] => CWgPqdn3YweN
        )

    [-MC09BsjtXP0izITsThf] => Array
        (
            [from] => CWgPqdn3YweN
            [message] => test message three
            [startedAt] => 2020-07-11 11:20:19
            [subject] => -MC00BHCZlXUp25C5nlS
            [to] => lSUfZ4sgEJd
        )

    [-MC1kAi1niswXtjY_h4s] => Array
        (
            [from] => CWgPqdn3YweN
            [message] => test message two
            [startedAt] => 2020-07-12 11:19:52
            [to] => lSUfZ4sgEJd
        )

    [-MC1kOtfnIlAYtmJsD-a] => Array
        (
            [from] => CWgPqdn3YweN
            [message] => test message one
            [startedAt] => 2020-07-12 11:18:50
            [to] => lSUfZ4sgEJd
        )
     [-MC1kOtfnIlAhgcufu] => Array
        (
            [from] => CWgPqdn3YweN
            [message] => test message zero
            [startedAt] => 2020-07-12 11:00:50
            [to] => YLisXjk07w93
        )
)

I want to get the latest, distinct conversations no matter if I'm the sender or receiver. The end result I want will look like this

Array
(
 [-MC1kTfrrDHY3ZbidJ4Q] => Array
        (
            [from] => lSUfZ4sgEJd
            [message] => test message four no more!
            [startedAt] => 2020-07-12 11:21:10
            [to] => CWgPqdn3YweN
        )
 [-MC1kOtfnIlAhgcufu] => Array
        (
            [from] => CWgPqdn3YweN
            [message] => test message zero
            [startedAt] => 2020-07-12 11:00:50
            [to] => YLisXjk07w93
        )
)

Anybody mind providing guidance on this? I have tried following along with responses on StackOverflow Get unique value of one attribute from array of associative arrays but I lose the $key of the array, which is the ID of the message.

markusp
  • 329
  • 1
  • 2
  • 10

1 Answers1

1

have a look at this solution: https://3v4l.org/msUPQ

I've added some comments along the code in case its not clear :)

Works as long as provided $data is ordered by most recent first (according to specs).

<?php

declare(strict_types=1);

$data = [
    '-MC1kTfrrDHY3ZbidJ4Q' => [
        'from' => 'lSUfZ4sgEJd',
        'message' => 'test message four no more!',
        'startedAt' => '2020-07-12 11:21:10',
        'to' => 'CWgPqdn3YweN',
    ],
    '-MC09BsjtXP0izITsThf' => [
        'from' => 'CWgPqdn3YweN',
        'message' => 'test message three',
        'startedAt' => '2020-07-11 11:20:19',
        'subject' => '-MC00BHCZlXUp25C5nlS',
        'to' => 'lSUfZ4sgEJd',
    ],
    '-MC1kAi1niswXtjY_h4s' => [
        'from' => 'CWgPqdn3YweN',
        'message' => 'test message two',
        'startedAt' => '2020-07-12 11:19:52',
        'to' => 'lSUfZ4sgEJd',
    ],
    '-MC1kOtfnIlAYtmJsD-a' => [
        'from' => 'CWgPqdn3YweN',
        'message' => 'test message one',
        'startedAt' => '2020-07-12 11:18:50',
        'to' => 'lSUfZ4sgEJd',
    ],
    '-MC1kOtfnIlAhgcufu' => [
        'from' => 'CWgPqdn3YweN',
        'message' => 'test message zero',
        'startedAt' => '2020-07-12 11:00:50',
        'to' => 'YLisXjk07w93',
    ],
];

function getLatestUniqueConversationMessagesByUser(array $message, string $user): array
{
    $conversations = [];
    $latestUniqueConversationMessages = [];
    
    foreach ($message as $messageKey => $message) {
        $participants = [$message['from'], $message['to']];
        
        // Skip entries where $user is has not participated (not sure if needed)
        if (!in_array($user, $participants)) { continue; }
        
        // Make "to|from" same as "from|to"
        sort($participants);
        $conversationKey = join('|', $participants);
        
        // Check if conversation has been handled already
        if (!array_key_exists($conversationKey, $conversations)) {
            $conversations[$conversationKey] = true; // Save as "handled"
            $latestUniqueConversationMessages[$messageKey] = $message; // Save actual data to return
        }
    }
    
    return $latestUniqueConversationMessages;
}

var_dump(getLatestUniqueConversationMessagesByUser($data, 'CWgPqdn3YweN'));

Output:

array(2) {
  ["-MC1kTfrrDHY3ZbidJ4Q"]=>
  array(4) {
    ["from"]=>
    string(11) "lSUfZ4sgEJd"
    ["message"]=>
    string(26) "test message four no more!"
    ["startedAt"]=>
    string(19) "2020-07-12 11:21:10"
    ["to"]=>
    string(12) "CWgPqdn3YweN"
  }
  ["-MC1kOtfnIlAhgcufu"]=>
  array(4) {
    ["from"]=>
    string(12) "CWgPqdn3YweN"
    ["message"]=>
    string(17) "test message zero"
    ["startedAt"]=>
    string(19) "2020-07-12 11:00:50"
    ["to"]=>
    string(12) "YLisXjk07w93"
  }
}

Alternative using array-functions (not that readable):

$messageKeys = 
    array_keys(
        array_unique(
            array_map(
                function ($message) {
                    $participants = [$message['from'], $message['to']];
                    sort($participants);
                    return join('|', $participants);
                },
                $data
            )
        )
    );

var_dump(
    array_filter(
        $data,
        function ($key) use ($messageKeys) {
            return in_array($key, $messageKeys);
        },
        ARRAY_FILTER_USE_KEY
    )
);
SirPilan
  • 4,649
  • 2
  • 13
  • 26