Skip to content

Commit 784ebca

Browse files
author
Jonathan Gaillard
committed
Merge pull request #41 from chadicus/master
Add Arrays::partition
2 parents 6f07338 + 92a567d commit 784ebca

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed

src/Util/Arrays.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,4 +332,43 @@ public static function getFirstSet(array $array, array $keys, $default = null)
332332

333333
return $default;
334334
}
335+
336+
/**
337+
* Partitions the given $input array into an array of $partitionCount sub arrays.
338+
*
339+
* This is a slight modification of the function suggested on http://php.net/manual/en/function.array-chunk.php#75022.
340+
* This method does not pad with empty partitions and ensures positive partition count.
341+
*
342+
* @param array $input The array to partition.
343+
* @param int $partitionCount The maximum number of partitions to create.
344+
* @param bool $preserveKeys Flag to preserve numeric array indexes. Associative indexes are preserved by default.
345+
*
346+
* @return array A multi-dimensional array containing $partitionCount sub arrays.
347+
*
348+
* @throws \InvalidArgumentException Thrown if $partitionCount is not a positive integer.
349+
* @throws \InvalidArgumentException Thrown if $preserveKeys is not a boolean value.
350+
*/
351+
public static function partition(array $input, $partitionCount, $preserveKeys = false)
352+
{
353+
if (!is_int($partitionCount) || $partitionCount < 1) {
354+
throw new \InvalidArgumentException('$partitionCount must be a positive integer');
355+
}
356+
357+
if ($preserveKeys !== false && $preserveKeys !== true) {
358+
throw new \InvalidArgumentException('$preserveKeys must be a boolean value');
359+
}
360+
361+
$inputLength = count($input);
362+
$partitionLength = floor($inputLength / $partitionCount);
363+
$partitionRemainder = $inputLength % $partitionCount;
364+
$partitions = [];
365+
$sliceOffset = 0;
366+
for ($partitionIndex = 0; $partitionIndex < $partitionCount && $sliceOffset < $inputLength; $partitionIndex++) {
367+
$sliceLength = ($partitionIndex < $partitionRemainder) ? $partitionLength + 1 : $partitionLength;
368+
$partitions[$partitionIndex] = array_slice($input, $sliceOffset, $sliceLength, $preserveKeys);
369+
$sliceOffset += $sliceLength;
370+
}
371+
372+
return $partitions;
373+
}
335374
}

tests/Util/ArraysTest.php

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,4 +561,150 @@ public function getFirstSet_withDefault()
561561
{
562562
$this->assertSame('baz', A::getFirstSet(['foo', null, 'bar'], [1, 4], 'baz'));
563563
}
564+
565+
/**
566+
* Verifiy basic behavior of partition()
567+
*
568+
* @test
569+
* @covers ::partition
570+
*/
571+
public function partition()
572+
{
573+
$this->assertSame([['a'], ['b'], ['c']], A::partition(['a', 'b', 'c'], 3));
574+
}
575+
576+
/**
577+
* Verify partition() behavior when $input array contains less items than than $partitionCount.
578+
*
579+
* @test
580+
* @covers ::partition
581+
*/
582+
public function partition_inputLessThanPartitionCount()
583+
{
584+
$this->assertSame([['a'], ['b'], ['c']], A::partition(['a', 'b', 'c'], 4));
585+
}
586+
587+
/**
588+
* Verify remainder of $input array is front-loaded in partition().
589+
*
590+
* @test
591+
* @covers ::partition
592+
*/
593+
public function partition_withRemainder()
594+
{
595+
$this->assertSame([['a', 'b'], ['c'], ['d']], A::partition(['a', 'b', 'c', 'd'], 3));
596+
}
597+
598+
/**
599+
* Verify remainder of $input array is front-loaded in partition().
600+
*
601+
* @test
602+
* @covers ::partition
603+
*/
604+
public function partition_withMultipleRemainder()
605+
{
606+
$this->assertSame([['a', 'b'], ['c', 'd'], ['e']], A::partition(['a', 'b', 'c', 'd', 'e'], 3));
607+
}
608+
609+
/**
610+
* Verify partition() handles empty $input array.
611+
*
612+
* @test
613+
* @covers ::partition
614+
*/
615+
public function partition_emptyInput()
616+
{
617+
$this->assertSame([], A::partition([], 2));
618+
}
619+
620+
/**
621+
* Verifiy behavior of partition() with $partitionCount of 1.
622+
*
623+
* @test
624+
* @covers ::partition
625+
*/
626+
public function partition_onePartition()
627+
{
628+
$this->assertSame([['a', 'b', 'c']], A::partition(['a', 'b', 'c'], 1));
629+
}
630+
631+
/**
632+
* Verifiy partition() throws with negative $partitionCount.
633+
*
634+
* @test
635+
* @covers ::partition
636+
* @expectedException \InvalidArgumentException
637+
* @expectedExceptionMessage $partitionCount must be a positive integer
638+
*/
639+
public function partition_negativePartitionCount()
640+
{
641+
A::partition(['a', 'b', 'c'], -1);
642+
}
643+
644+
/**
645+
* Verifiy partition() throws with 0 $partitionCount.
646+
*
647+
* @test
648+
* @covers ::partition
649+
* @expectedException \InvalidArgumentException
650+
* @expectedExceptionMessage $partitionCount must be a positive integer
651+
*/
652+
public function partition_zeroPartitionCount()
653+
{
654+
A::partition(['a', 'b', 'c'], 0);
655+
}
656+
657+
/**
658+
* Verifiy partition() throws with non-integer $partitionCount.
659+
*
660+
* @test
661+
* @covers ::partition
662+
* @expectedException \InvalidArgumentException
663+
* @expectedExceptionMessage $partitionCount must be a positive integer
664+
*/
665+
public function partition_nonIntegerPartitionCount()
666+
{
667+
A::partition(['a', 'b', 'c'], 'not an int');
668+
}
669+
670+
/**
671+
* Verifiy partition() preserves numeric keys.
672+
*
673+
* @test
674+
* @covers ::partition
675+
*/
676+
public function partition_preserveNumericKeys()
677+
{
678+
$this->assertSame(
679+
[[0 => 'a', 1 => 'b'], [2 => 'c', 3 => 'd'], [4 => 'e']],
680+
A::partition(['a', 'b', 'c', 'd', 'e'], 3, true)
681+
);
682+
}
683+
684+
/**
685+
* Verifiy partition() preserves associative keys.
686+
*
687+
* @test
688+
* @covers ::partition
689+
*/
690+
public function partition_preserveAssociativeKeys()
691+
{
692+
$this->assertSame(
693+
[['a' => 0, 'b' => 1], ['c' => 2, 'd' => 3], ['e' => 4]],
694+
A::partition(['a' => 0, 'b' => 1, 'c' => 2, 'd' => 3, 'e' => 4], 3)
695+
);
696+
}
697+
698+
/**
699+
* Verifiy partition() throws with non-boolean $preserveKeys.
700+
*
701+
* @test
702+
* @covers ::partition
703+
* @expectedException \InvalidArgumentException
704+
* @expectedExceptionMessage $preserveKeys must be a boolean value
705+
*/
706+
public function partition_nonBoolPreserveKeys()
707+
{
708+
A::partition(['a', 'b', 'c'], 3, 'not a bool');
709+
}
564710
}

0 commit comments

Comments
 (0)