Skip to content

Commit

Permalink
Merge pull request traderinteractive#41 from chadicus/master
Browse files Browse the repository at this point in the history
Add Arrays::partition
  • Loading branch information
Jonathan Gaillard committed Aug 27, 2014
2 parents 6f07338 + 92a567d commit 784ebca
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 0 deletions.
39 changes: 39 additions & 0 deletions src/Util/Arrays.php
Original file line number Diff line number Diff line change
Expand Up @@ -332,4 +332,43 @@ public static function getFirstSet(array $array, array $keys, $default = null)

return $default;
}

/**
* Partitions the given $input array into an array of $partitionCount sub arrays.
*
* This is a slight modification of the function suggested on http://php.net/manual/en/function.array-chunk.php#75022.
* This method does not pad with empty partitions and ensures positive partition count.
*
* @param array $input The array to partition.
* @param int $partitionCount The maximum number of partitions to create.
* @param bool $preserveKeys Flag to preserve numeric array indexes. Associative indexes are preserved by default.
*
* @return array A multi-dimensional array containing $partitionCount sub arrays.
*
* @throws \InvalidArgumentException Thrown if $partitionCount is not a positive integer.
* @throws \InvalidArgumentException Thrown if $preserveKeys is not a boolean value.
*/
public static function partition(array $input, $partitionCount, $preserveKeys = false)
{
if (!is_int($partitionCount) || $partitionCount < 1) {
throw new \InvalidArgumentException('$partitionCount must be a positive integer');
}

if ($preserveKeys !== false && $preserveKeys !== true) {
throw new \InvalidArgumentException('$preserveKeys must be a boolean value');
}

$inputLength = count($input);
$partitionLength = floor($inputLength / $partitionCount);
$partitionRemainder = $inputLength % $partitionCount;
$partitions = [];
$sliceOffset = 0;
for ($partitionIndex = 0; $partitionIndex < $partitionCount && $sliceOffset < $inputLength; $partitionIndex++) {
$sliceLength = ($partitionIndex < $partitionRemainder) ? $partitionLength + 1 : $partitionLength;
$partitions[$partitionIndex] = array_slice($input, $sliceOffset, $sliceLength, $preserveKeys);
$sliceOffset += $sliceLength;
}

return $partitions;
}
}
146 changes: 146 additions & 0 deletions tests/Util/ArraysTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -561,4 +561,150 @@ public function getFirstSet_withDefault()
{
$this->assertSame('baz', A::getFirstSet(['foo', null, 'bar'], [1, 4], 'baz'));
}

/**
* Verifiy basic behavior of partition()
*
* @test
* @covers ::partition
*/
public function partition()
{
$this->assertSame([['a'], ['b'], ['c']], A::partition(['a', 'b', 'c'], 3));
}

/**
* Verify partition() behavior when $input array contains less items than than $partitionCount.
*
* @test
* @covers ::partition
*/
public function partition_inputLessThanPartitionCount()
{
$this->assertSame([['a'], ['b'], ['c']], A::partition(['a', 'b', 'c'], 4));
}

/**
* Verify remainder of $input array is front-loaded in partition().
*
* @test
* @covers ::partition
*/
public function partition_withRemainder()
{
$this->assertSame([['a', 'b'], ['c'], ['d']], A::partition(['a', 'b', 'c', 'd'], 3));
}

/**
* Verify remainder of $input array is front-loaded in partition().
*
* @test
* @covers ::partition
*/
public function partition_withMultipleRemainder()
{
$this->assertSame([['a', 'b'], ['c', 'd'], ['e']], A::partition(['a', 'b', 'c', 'd', 'e'], 3));
}

/**
* Verify partition() handles empty $input array.
*
* @test
* @covers ::partition
*/
public function partition_emptyInput()
{
$this->assertSame([], A::partition([], 2));
}

/**
* Verifiy behavior of partition() with $partitionCount of 1.
*
* @test
* @covers ::partition
*/
public function partition_onePartition()
{
$this->assertSame([['a', 'b', 'c']], A::partition(['a', 'b', 'c'], 1));
}

/**
* Verifiy partition() throws with negative $partitionCount.
*
* @test
* @covers ::partition
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage $partitionCount must be a positive integer
*/
public function partition_negativePartitionCount()
{
A::partition(['a', 'b', 'c'], -1);
}

/**
* Verifiy partition() throws with 0 $partitionCount.
*
* @test
* @covers ::partition
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage $partitionCount must be a positive integer
*/
public function partition_zeroPartitionCount()
{
A::partition(['a', 'b', 'c'], 0);
}

/**
* Verifiy partition() throws with non-integer $partitionCount.
*
* @test
* @covers ::partition
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage $partitionCount must be a positive integer
*/
public function partition_nonIntegerPartitionCount()
{
A::partition(['a', 'b', 'c'], 'not an int');
}

/**
* Verifiy partition() preserves numeric keys.
*
* @test
* @covers ::partition
*/
public function partition_preserveNumericKeys()
{
$this->assertSame(
[[0 => 'a', 1 => 'b'], [2 => 'c', 3 => 'd'], [4 => 'e']],
A::partition(['a', 'b', 'c', 'd', 'e'], 3, true)
);
}

/**
* Verifiy partition() preserves associative keys.
*
* @test
* @covers ::partition
*/
public function partition_preserveAssociativeKeys()
{
$this->assertSame(
[['a' => 0, 'b' => 1], ['c' => 2, 'd' => 3], ['e' => 4]],
A::partition(['a' => 0, 'b' => 1, 'c' => 2, 'd' => 3, 'e' => 4], 3)
);
}

/**
* Verifiy partition() throws with non-boolean $preserveKeys.
*
* @test
* @covers ::partition
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage $preserveKeys must be a boolean value
*/
public function partition_nonBoolPreserveKeys()
{
A::partition(['a', 'b', 'c'], 3, 'not a bool');
}
}

0 comments on commit 784ebca

Please sign in to comment.