Search code examples
phparraysduplicatesdistributedchunks

php array chunk distribute duplicates


I would like to order people in balanced groups, but these people belong to a team. I want to know if it is possible to generate groups, but prevent more people from the same team are in the same group.

Example array (people => team)

$total = array(
    'Kitten01'  => 'A',
    'Kitten02'  => 'A',
    'Kitten03'  => 'U',
    'Kitten04'  => 'U',
    'Kitten05'  => 'B',
    'Kitten06'  => 'B',
    'Kitten07'  => 'M',
    'Kitten08'  => 'M',
    'Kitten09'  => 'C',
    'Kitten10'  => 'C',
    'Kitten11'  => 'Y',
);

I've used this function:

function partition( $list, $p ) {
    $listlen = count( $list );
    $partlen = floor( $listlen / $p );
    $partrem = $listlen % $p;
    $partition = array();
    $mark = 0;
    for ($px = 0; $px < $p; $px++) {
        $incr = ($px < $partrem) ? $partlen + 1 : $partlen;
        $partition[$px] = array_slice( $list, $mark, $incr );
        $mark += $incr;
    }
    return $partition;
}

And finally I generate groups:

$max_group  = 8;
$people     = count($total);
$groups     = ceil($people / $max_group);

print_r(partition($total, $groups)) ;   

.. and returns this:

Array
(
    [0] => Array
        (
            [Kitten01] => A
            [Kitten02] => A
            [Kitten03] => U
            [Kitten04] => U
            [Kitten05] => B
            [Kitten06] => B
        )

    [1] => Array
        (
            [Kitten07] => M
            [Kitten08] => M
            [Kitten09] => C
            [Kitten10] => C
            [Kitten11] => Y
        )

)

Is possible to return this?:

 Array
(
    [0] => Array
        (
            [Kitten01] => A
            [Kitten03] => U
            [Kitten05] => B               
            [Kitten07] => M
            [Kitten09] => C
            [Kitten11] => Y 
        )

    [1] => Array
        (
            [Kitten02] => A
            [Kitten04] => U
            [Kitten06] => B                
            [Kitten08] => M                
            [Kitten10] => C
        )

)

I hope your help

** Edit: Solution **

  1. With the William's function flip(), I classify all items per team.

  2. with the make_group() function, I take only one Kitten per team and I create a group, the next time that use the make_group() function, I take the rest.

  3. Finally groups are ordered sequentially by team G1(A,U,B,M,C,...), G2(A,U,B,M,C,...) ...

  4. If I merge all these groups and I divide proportionally (if exceeds the maximum permitted per group: 8) I'll never have two people in the same team:

** Final Code **

# groups quantity
$max_group      = 8;
$people         = count($total);
$groups         = ceil($people / $max_group);
$total          = flip($total);
$total_group    = array();

# merge groups
for($y=0;$y<$groups;$y++)
{
    $total_group = array_merge($total_group, make_group($total));
}

# .. and divide proportionally
print_r(partition($total_group, $groups));  

Solution

  • The following should work, I believe:

    // Your input array
    $total = array(
        'Kitten01'  => 'A',
        'Kitten02'  => 'A',
        'Kitten03'  => 'U',
        'Kitten04'  => 'U',
        'Kitten05'  => 'B',
        'Kitten06'  => 'B',
        'Kitten07'  => 'M',
        'Kitten08'  => 'M',
        'Kitten09'  => 'C',
        'Kitten10'  => 'C',
        'Kitten11'  => 'Y',
    );
    
    // Helper function that flips values and keys, so that your array
    // would look like:
    //     array(
    //         'A' => array('Kitten01', 'Kitten02'),
    //         'U' => array('Kitten03', 'Kitten04'),
    //         'B' => array('Kitten05', 'Kitten06'),
    //          ...
    function flip($array) {
        $result = array();
    
        foreach ($array as $k => $v) {
            $result[$v][] = $k;
        }
    
        return $result;
    }
    
    // Make a group from a set of remaining members in teams
    function make_group(&$teams) {
        $group = array();
    
        // Pick one member per team
        foreach ($teams as $k => &$v) {
            // If that team still has members, remove the member from the
            // team and add it to the group
            if ($member = array_shift($v)) {
                $group[$member] = $k;
             }
        }
    
        return $group;
    }
    
    $teams = flip($total);
    // Repeat as needed
    print_r(make_group($teams));
    print_r(make_group($teams));