0

Problem

I have the following array, consisting of N different services, where each entry consists of an identifier and a unique (user)name.

$input = [
    'service_1' => [
        '1234' => 'John_Doe_1',
        '4567' => 'Jane Doe X',
        '7891' => 'J.Doe1',
    ],
    'service_2' => [
        '100001' => 'Jane Doe X',
        '100002' => 'John_Doe_1',
        '100003' => 'J.Doe1',
    ],
    'service_N' => [
        '07faed21-2920-4d7d-a263-88deba9c422c' => 'John_Doe_1',
        '1160178c-dfbf-4091-b4c0-a8ec55c22800' => 'J.Doe1',
    ],
];

Now I'm looking for a way to format it in a way that I get the identifiers across each (user)name for the different services:

$output = [
    'John_Doe_1' => [
        'service_1' => '1234',
        'service_2' => '100002',
        'service_N' => '07faed21-2920-4d7d-a263-88deba9c422c',
    ],
    'Jane Doe X' => [
        'service_1' => '4567',
        'service_2' => '100001',
        'service_N' => null, // either value should be null or key should not exist
    ],
    'J.Doe1' => [
        'service_1' => '7891',
        'service_2' => '100003',
        'service_N' => '1160178c-dfbf-4091-b4c0-a8ec55c22800',
    ],
];

I'm looking for a flexible way (with N services) to do this but I can't come up with a good solution.

YTZ
  • 876
  • 11
  • 26
  • The accepted answer was correct and can even be edited to include the key with null value, which can be useful in some situations. Thanks but I'll rather revert this edit. – YTZ Jan 31 '22 at 23:30

2 Answers2

1

The code below should do the trick. This is how it works.

  1. Loops over the keys of the outer array to get the keys for the output array.
  2. Loops over the key value pair within every inner array.
  3. Creates an empty array in the output array with the username as key if it does not exist.
  4. Adds the 'ID' of the server under the name that we created earlier under the key it's currently looping through.

This should also still work within a reasonable time if your array input gets really big (e.g. 10000 elements).

$output = [];

// Loop over service_1, service_2, service_N etc.
foreach(array_keys($input) as $service_name)
{
    // Loop over the inner key value pair (e.g. 10001 => John Doe X)
    foreach($input[$service_name] as $service_id => $username)
    {
        // Create a key with the name if it does not exist in the output.
        if(!isset($output[$username]))
        {
            $output[$username] = [];
        }

        // Add the key value pair to the correct output name.
        $output[$username][$service_name] = $service_id;
    }
}

That code will produce the following output.

Array
(
    [John_Doe_1] => Array
        (
            [service_1] => 1234
            [service_2] => 100002
            [service_N] => 07faed21-2920-4d7d-a263-88deba9c422c
        )

    [Jane Doe X] => Array
        (
            [service_1] => 4567
            [service_2] => 100001
        )

    [J.Doe1] => Array
        (
            [service_1] => 7891
            [service_2] => 100003
            [service_N] => 1160178c-dfbf-4091-b4c0-a8ec55c22800
        )

)
D1__1
  • 925
  • 1
  • 3
  • 14
  • 1
    When it comes to explanation, readability and descriptive variables, I think this would be the accepted answer. Just a tiny nitpick (which I probably should've clarified better), `$service_id` would be better to be renamed as `$user_id` as that's what the identifier represents. And I had the identifiers as strings on purpose, so still needs casting: `(string) $service_id`. One more plus: unlike the other provided answers, there's no passing as reference involved, which I personally like more. – YTZ Jan 16 '22 at 13:57
  • @YTZ: What about missing keys as you mentioned in your question? service_N should present in Jane Doe X? – Ravi Hirani Jan 17 '22 at 03:52
  • @Ravi that was optional, either being null or key not existing is fine. – YTZ Jan 29 '22 at 15:49
  • @Ravi To be fair, I had already specified this with a comment in the second code block... – YTZ Jan 31 '22 at 20:07
  • @YTZ: You must be kidding. It should be part of the question not commented code :) – Ravi Hirani Jan 31 '22 at 20:32
1

I've been on a functional programming kick recently and figured I'd dive into PHP to see what I could come up with. Here's a nested array_walk method that seems to do the trick!

$output = Array();
array_walk($input, function($item, $key) use (&$output) {
    array_walk($item, function($item, $key, $parent_key) use (&$output) {
        $output[$parent_key][$item] = $key;
    }, $key);
});
var_dump($output);
Spencer May
  • 4,266
  • 9
  • 28
  • 48
  • 1
    This is actually pretty neat, and I would've accepted this in an instance a year or so ago but the moment I started working in a team, I came to realize that these type of snippets can make it harder for other people to read or debug code compared to using a simple `foreach` loop. I think other people can still benefit from your answer (which is why I upvoted it). – YTZ Jan 16 '22 at 14:13