Nikola
Nikola

Reputation: 15038

Equal distribution of items in an array comprised of three arrays

Say you have three arrays like this:

$first  = array('f1', 'f2', 'f3');
$second = array('s1', 's2', 's3', 's4', 's5');
$third  = array('t1', 't2', 't3', 't4', 't5', 't6', 't7', 't8', 't9', 't10', 't11', 't12');

Each array can have from 0 to exactly 12 items. The task is to gather an array of maximum 12 items which compose of equally distributed number of items from these three arrays. In this particular example the wanted output would be:

array("f1", "f2", "f3", "s1", "s2", "s3", "s4", "t1", "t2", "t3", "t4", "t5")

So, the final array should ideally have 4 items of each array (12 wanting items / 3 array = 4 items from each array). In the upper example since the first array has only 3 items I "compensate" with increasing the number of items from other arrays.

I came up with the function to do this:

function calcArrays($arrays, $minItems){
    $finalArr = array();

    $first = $arrays["first"];
    $firstCount = count($first);

    $second = $arrays["second"];
    $secondCount = count($second);

    $third = $arrays["third"];
    $thirdCount = count($third);

    $totalCount = $firstCount + $secondCount + $thirdCount;

    if ($totalCount == 0){
        return array();
    }
    if ($totalCount == 36){
        for($i=0; $i<$minItems; $i++)
            $finalArr[] = $first[$i];

        for($i=0; $i<$minItems; $i++)
            $finalArr[] = $second[$i];

        for($i=0; $i<$minItems; $i++)
            $finalArr[] = $third[$i];

        return $finalArr;
    }

    if ($firstCount < $secondCount && $firstCount < $thirdCount){
         if ($firstCount < $minItems){
             for ($i=0; $i<$firstCount; $i++)
                $finalArr[] = $first[$i];
         }
         else{
             for ($i=0; $i<$minItems; $i++)
                $finalArr[] = $first[$i];
         }

         if ($secondCount < $thirdCount){
             if ($secondCount < $minItems){
                 for ($i=0; $i<$secondCount; $i++)
                    $finalArr[] = $second[$i];
             }
             else{
                 for ($i=0; $i<$minItems; $i++)
                    $finalArr[] = $second[$i];
             }

             $howManyLeftTillFull = count($arrays) * $minItems - count($finalArr);
             if ($thirdCount < $howManyLeftTillFull){
                 for ($i=0; $i<$thirdCount; $i++)
                    $finalArr[] = $third[$i];
             }
             else{
                 for ($i=0; $i<$howManyLeftTillFull; $i++)
                    $finalArr[] = $third[$i];
             }
         }
         else{
             if ($thirdCount < $minItems){
                 for ($i=0; $i<$thirdCount; $i++)
                    $finalArr[] = $third[$i];
             }
             else{
                 for ($i=0; $i<$minItems; $i++)
                    $finalArr[] = $third[$i];
             }

             $howManyLeftTillFull = count($arrays) * $minItems - count($finalArr);
             if ($secondCount < $howManyLeftTillFull){
                 for ($i=0; $i<$secondCount; $i++)
                    $finalArr[] = $second[$i];
             }
             else{
                 for ($i=0; $i<$howManyLeftTillFull; $i++)
                    $finalArr[] = $second[$i];
             }
         }

         return $finalArr;
    }
    else if ($secondCount < $firstCount && $secondCount < $thirdCount){
        //note to myself: there's gotta be a better way :/    
    }
    else if ($thirdCount < $firstCount && $thirdCount < $secondCount){
        //see the upper note to yourself and just don't go this way ^_^
    }
}

and a way to call them would be:

$arrays = array("first" => $first, "second" => $second, "third" => $third);
$finalArr = calcArrays($arrays, 4);

So, I made the first case scenario where the first array has the smallest number of items, then follows the second and finally the third. As you can see my comment in the other two else ifs - this just has to be done in a better and more readable way. Because, sure, I can make this work if I continue down this path but I would eventually hate myself when rereading the code.

So, can someone point me in the maybe existing algorithmic problem or give some better idea in how to approach this?

Upvotes: 0

Views: 580

Answers (1)

kero
kero

Reputation: 10638

So, the final array should ideally have 4 items of each array (12 wanting items / 3 array = 4 items from each array). In the upper example since the first array has only 3 items I "compensate" with increasing the number of items from other arrays.

If an open space exists, which array should fill it? You might want spaces to be equally distributed, then I would suggest writing a complete class for this, calculating and creating the result in different steps.

Given your example, it should be okay to let the following array fill the missed spaces if possible. The task becomes quite simple then.

$data = array(
    array('f1', 'f2', 'f3'),
    array('s1', 's2', 's3', 's4', 's5'),
    array('t1', 't2', 't3', 't4', 't5', 't6', 't7', 't8', 't9', 't10', 't11', 't12')
);

$max = 12;

$each = $max / count($data);
$result = array();

$missing = 0;
foreach ($data as $set) {
    $missing += $each;
    while (count($set) > 0 && $missing > 0) {
        $result[] = array_shift($set);
        $missing--;
    }
}

print_r($result);

Since you know $max and the number of input arrays, calculating $each is easy beforehand. Now this while() will go as long as the input array has items and result needs items. If the item has less items, $missing will carry that on to the next iteration. This might however run into problems if the last array has too little items but should give you an idea

Upvotes: 2

Related Questions