Skip to content

Commit

Permalink
Add cartesian_product and InvalidArgumentException::assertStringable
Browse files Browse the repository at this point in the history
  • Loading branch information
someonewithpc committed Dec 10, 2021
1 parent 5f5ddb6 commit 4b1269a
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 0 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"src/Functional/Average.php",
"src/Functional/ButLast.php",
"src/Functional/Capture.php",
"src/Functional/CartesianProduct.php",
"src/Functional/ConstFunction.php",
"src/Functional/CompareOn.php",
"src/Functional/CompareObjectHashOn.php",
Expand Down
62 changes: 62 additions & 0 deletions src/Functional/CartesianProduct.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

/**
* @package Functional-php
* @author Hugo Sales <hugo@hsal.es>
* @copyright 2021 Lars Strojny
* @license https://opensource.org/licenses/MIT MIT
* @link https://github.com/lstrojny/functional-php
*/

namespace Functional;

use Functional\Exceptions\InvalidArgumentException;
use Traversable;

/**
* @param string|array $separator
* @param array ...$collections
* @return array
* @no-named-arguments
*/
function cartesian_product($separator, ...$collections)
{
InvalidArgumentException::assertIntegerGreaterThanOrEqual(\count($collections), 2, __FUNCTION__, 2); // TODO not great

$aggregation = [];
$left = \array_shift($collections);
$index = 2;
InvalidArgumentException::assertCollection($left, __FUNCTION__, $index);
while (true) {
$right = \array_shift($collections);
InvalidArgumentException::assertCollection($right, __FUNCTION__, $index + 1);
$left_index = 0;
foreach ($left as $l) {
$right_index = 0;
foreach ($right as $r) {
InvalidArgumentException::assertStringable($l, __FUNCTION__, $index, $left_index);
InvalidArgumentException::assertStringable($r, __FUNCTION__, $index + 1, $right_index);
if (\is_string($separator)) {
$aggregation[] = "{$l}{$separator}{$r}";
} else if (\is_array($separator)) {
foreach ($separator as $sep) {
$aggregation[] = "{$l}{$sep}{$r}";
}
} else {
// TODO assert that $separator is string or array of strings
}
++$right_index;
}
++$left_index;
}
++$index;
if (empty($collections)) {
break;
} else {
$left = $aggregation;
$aggregation = [];
}
}

return $aggregation;
}
11 changes: 11 additions & 0 deletions src/Functional/Exceptions/InvalidArgumentException.php
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,17 @@ public static function assertPair($pair, $callee, $position): void
}
}

public static function assertStringable($value, $callee, $position, $positionInCollection = null): void
{
if (!(\is_string($value) || \is_numeric($value) || (\is_object($value) && \method_exists($value, '__toString')))) {
if (!\is_null($positionInCollection)) {
throw new static(\sprintf('%s() expects paramter %d to be an array with strings or objects with a __toString method (offending index: %d is of type %s)', $callee, $position, $positionInCollection, self::getType($value)));
} else {
throw new static(\sprintf('%s() expects paramter %d to be a string or an object with a __toString method (got type %s)', $callee, $position, self::getType($value)));
}
}
}

private static function getType($value)
{
return \is_object($value) ? \get_class($value) : \gettype($value);
Expand Down
5 changes: 5 additions & 0 deletions src/Functional/Functional.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ final class Functional
*/
const capture = '\Functional\capture';

/**
* @see \Functional\cartesian_product
*/
const cartesian_product = '\Functional\cartesian_product';

/**
* @see \Functional\compare_object_hash_on
*/
Expand Down
24 changes: 24 additions & 0 deletions tests/Functional/CartesianProductTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

/**
* @package Functional-php
* @author Hugo Sales <hugo@hsal.es>
* @copyright 2021 Lars Strojny
* @license https://opensource.org/licenses/MIT MIT
* @link https://github.com/lstrojny/functional-php
*/

namespace Functional\Tests;

use function Functional\cartesian_product;

class CartesianProductTest extends AbstractTestCase
{
public function testCartesianProduct(): void
{
self::assertSame(['1-one', '1-two', '2-one', '2-two'], cartesian_product('-', [1, 2], ['one', 'two']));
self::assertSame(['1-one', '1_one', '1-two', '1_two', '2-one', '2_one', '2-two', '2_two'], cartesian_product(['-', '_'], [1, 2], ['one', 'two']));
self::assertSame(['1one', '1two', '2one', '2two'], cartesian_product('', [1, 2], ['one', 'two']));
self::assertSame(['1-one-H', '1-one-He', '1-two-H', '1-two-He', '2-one-H', '2-one-He', '2-two-H', '2-two-He'], cartesian_product('-', [1, 2], ['one', 'two'], ['H', 'He']));
}
}
30 changes: 30 additions & 0 deletions tests/Functional/Exceptions/InvalidArgumentExceptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,34 @@ public function testAssertPairWithThreeCharacterString(): void
$this->expectExceptionMessage('func() expects paramter 1 to be a pair (array with two elements)');
InvalidArgumentException::assertPair('abc', "func", 1);
}

public function testAssertStringableValid(): void
{
$this->expectNotToPerformAssertions();
InvalidArgumentException::assertStringable('abc', "func", 1);
InvalidArgumentException::assertStringable(1, "func", 1);
InvalidArgumentException::assertStringable(1.2, "func", 1);
InvalidArgumentException::assertStringable(1.2, "func", 1);
InvalidArgumentException::assertStringable(
new class
{
public function __toString()
{
}
},
"func",
1
);
}

public function testAssertStringableInvalid(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('func() expects paramter 1 to be a string or an object with a __toString method (got type boolean)');
InvalidArgumentException::assertStringable(false, "func", 1);
$this->expectExceptionMessage('func() expects paramter 1 to be a string or an object with a __toString method (got type array)');
InvalidArgumentException::assertStringable([], "func", 1);
$this->expectExceptionMessage('func() expects paramter 1 to be a string or an object with a __toString method (got type stdClass)');
InvalidArgumentException::assertStringable(new stdClass(), "func", 1);
}
}

0 comments on commit 4b1269a

Please sign in to comment.