Search code examples
phparraysforeachmany-to-manygrouping

Regroup multidimensional array to reverse presentation of many-to-many relationship


I need to perform iterated explosions on values in one column of my two dimensional array, then re-group the data to flip the relational presentation from "tag name -> video id" to "video id -> tag name".

Here is my input array:

$allTags = [
    [
        "name" => "TAG-ONE",
        "video" => "64070,64076,64110,64111",
    ],
    [
        "name" => "TAG-TWO",
        "video" => "64070,64076,64110,64111",
    ],
    [
        "name" => "TAG-THREE",
        "video" => "64111",
    ]
];

I want to isolate unique video ids and consolidate all tag names (as comma-separayed values) that relate to each video id.

Expected output:

$allTagsResult = [
    [
        "name" => "TAG-ONE,TAG-TWO",
        "video" => "64070",
    ],
    [
        "name" => "TAG-ONE,TAG-TWO",
        "video" => "64076",
    ],
    [
        "name" => "TAG-ONE,TAG-TWO",
        "video" => "64110",
    ],
    [
        "name" => "TAG-ONE,TAG-TWO,TAG-THREE",
        "video" => "64111",
    ],
];

Somehow I did it by checking the value using nested loops but I wish to know if you guys can suggest any shortest method to get the expected output.


Solution

  • If you want to completely remove foreach() loops, then using array_map(), array_walk_recursive(), array_fill_keys() etc. can do the job. Although I think that a more straightforward answer using foreach() would probably be faster, but anyway...

    $out1 = array_map(function ($data) { 
            return array_fill_keys(explode(",", $data['video']), $data['name']); },
        $allTags);
    
    $out2 = [];
    array_walk_recursive( $out1, function ( $data, $key ) use (&$out2)   {
        if ( isset($out2[$key]))    {
            $out2[$key]['name'] .= ",".$data;
        }
        else    {
            $out2[$key] = [ 'name' => $data, 'video' => $key ];
        }
    } );
    print_r($out2);
    

    will give...

    Array
    (
        [64070] => Array
            (
                [name] => TAG-ONE,TAG-TWO
                [video] => 64070
            )
    
        [64076] => Array
            (
                [name] => TAG-ONE,TAG-TWO
                [video] => 64076
            )
    
        [64110] => Array
            (
                [name] => TAG-ONE,TAG-TWO
                [video] => 64110
            )
    
        [64111] => Array
            (
                [name] => TAG-ONE,TAG-TWO,TAG-THREE
                [video] => 64111
            )
    
    )
    

    if you want to remove the keys, then

    print_r(array_values($out2));
    

    The code could be compressed by piling all of the code onto single lines, but readability is more useful sometimes.