Created
April 10, 2014 15:56
-
-
Save mcrumley/10396818 to your computer and use it in GitHub Desktop.
Partition an array into approximately equal-sized chunks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Partition an array into N chunks | |
* | |
* @param array $a array to split | |
* @param int $np number of partitions | |
* @param bool $pad pad the result with empty arrays if necessary (default = true) | |
* | |
* @return array the original array split into $np chunks | |
* | |
* Examples: | |
* array_partition(array(1, 2, 3), 2) -> array(array(1, 2), array(3)) | |
* array_partition(array(1, 2, 3), 4) -> array(array(1), array(2), array(3), array()) | |
* array_partition(array(1, 2, 3), 4, false) -> array(array(1), array(2), array(3)) | |
*/ | |
function array_partition(array $a, $np, $pad = true) | |
{ | |
$np = (int)$np; | |
if ($np <= 0) { | |
trigger_error('partition count must be greater than zero', E_USER_NOTICE); | |
return array(); | |
} | |
$c = count($a); | |
$per_array = (int)floor($c / $np); | |
$rem = $c % $np; | |
// special case for an empty array | |
if ($c === 0) { | |
if ($pad) { | |
$result = array_fill(0, $np, array()); | |
} else { | |
$result = array(); | |
} | |
} | |
// array_chunk will work if the remainder is 0 or np-1, or if there are more partitions than elements in the array | |
elseif ($rem === 0 || $rem == $np - 1 || $np >= $c) { | |
// if there is a remainder each partition will need 1 more | |
$result = array_chunk($a, $per_array + ($rem > 0 ? 1 : 0)); | |
// if necessary, pad out the array with empty arrays | |
if ($pad && $np > $c) { | |
$result = array_merge($result, array_fill(0, $np - $c, array())); | |
} | |
} | |
// use the slower case if 0 < remainder < np-1 and there are more elements in the array than paritions | |
// ($rem > 0 && $rem < $np - 1 && $np < $c) | |
else { | |
$split = $rem * ($per_array + 1); | |
// the first $rem partitions will have $per_array + 1 | |
$result = array_chunk(array_slice($a, 0, $split), $per_array+1); | |
// the rest of the partitions will have per_array | |
$result = array_merge($result, array_chunk(array_slice($a, $split), $per_array)); | |
// no padding is necessary if the conditions for this case are met | |
} | |
return $result; | |
} | |
function check_array_partition(array $a, $np, array $orig) { | |
// make sure the partition count is correct | |
if (count($a) !== $np) { | |
return false; | |
} | |
// accumulator - rebuild the original array | |
$acc = array(); | |
// remember the first partition size | |
$last = count($a) ? count($a[0]) : 0; | |
// go through each partition | |
foreach ($a as $part) { | |
// make sure the partition size never decreases by more than 1 | |
if (count($part) > $last || count($part) < $last - 1) { | |
echo 'array_partition(array['.count($orig).'], '.$np.') FAILED'; | |
return false; | |
} | |
foreach ($part as $int) { | |
$acc []= $int; | |
} | |
$last = count($part); | |
} | |
// make sure all the values are in the array and in the correct order | |
if ($np > 0 && $orig === $acc) { | |
return true; | |
// if partition count is zero, there should be nothing in the accumulator | |
} elseif ($np === 0 && $acc === array()) { | |
return true; | |
} else { | |
echo 'array_partition(array['.count($orig).'], '.$np.') FAILED'; | |
return false; | |
} | |
} | |
foreach (range(0, 10) as $partitions) { | |
if ($partitions === 0) { | |
error_reporting(~E_USER_NOTICE); | |
} else { | |
error_reporting(E_ALL); | |
} | |
check_array_partition(array_partition(array(), $partitions), $partitions, array()); | |
check_array_partition(array_partition(array(0), $partitions), $partitions, array(0)); | |
foreach (range(1, 6) as $array_size) { | |
check_array_partition(array_partition(range(0, $array_size), $partitions), $partitions, range(0, $array_size)); | |
} | |
} | |
echo ' DONE'; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment