Skip to content

Commit 4b1269a

Browse files
committed
Add cartesian_product and InvalidArgumentException::assertStringable
1 parent 5f5ddb6 commit 4b1269a

File tree

6 files changed

+133
-0
lines changed

6 files changed

+133
-0
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"src/Functional/Average.php",
3434
"src/Functional/ButLast.php",
3535
"src/Functional/Capture.php",
36+
"src/Functional/CartesianProduct.php",
3637
"src/Functional/ConstFunction.php",
3738
"src/Functional/CompareOn.php",
3839
"src/Functional/CompareObjectHashOn.php",
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
/**
4+
* @package Functional-php
5+
* @author Hugo Sales <hugo@hsal.es>
6+
* @copyright 2021 Lars Strojny
7+
* @license https://opensource.org/licenses/MIT MIT
8+
* @link https://github.com/lstrojny/functional-php
9+
*/
10+
11+
namespace Functional;
12+
13+
use Functional\Exceptions\InvalidArgumentException;
14+
use Traversable;
15+
16+
/**
17+
* @param string|array $separator
18+
* @param array ...$collections
19+
* @return array
20+
* @no-named-arguments
21+
*/
22+
function cartesian_product($separator, ...$collections)
23+
{
24+
InvalidArgumentException::assertIntegerGreaterThanOrEqual(\count($collections), 2, __FUNCTION__, 2); // TODO not great
25+
26+
$aggregation = [];
27+
$left = \array_shift($collections);
28+
$index = 2;
29+
InvalidArgumentException::assertCollection($left, __FUNCTION__, $index);
30+
while (true) {
31+
$right = \array_shift($collections);
32+
InvalidArgumentException::assertCollection($right, __FUNCTION__, $index + 1);
33+
$left_index = 0;
34+
foreach ($left as $l) {
35+
$right_index = 0;
36+
foreach ($right as $r) {
37+
InvalidArgumentException::assertStringable($l, __FUNCTION__, $index, $left_index);
38+
InvalidArgumentException::assertStringable($r, __FUNCTION__, $index + 1, $right_index);
39+
if (\is_string($separator)) {
40+
$aggregation[] = "{$l}{$separator}{$r}";
41+
} else if (\is_array($separator)) {
42+
foreach ($separator as $sep) {
43+
$aggregation[] = "{$l}{$sep}{$r}";
44+
}
45+
} else {
46+
// TODO assert that $separator is string or array of strings
47+
}
48+
++$right_index;
49+
}
50+
++$left_index;
51+
}
52+
++$index;
53+
if (empty($collections)) {
54+
break;
55+
} else {
56+
$left = $aggregation;
57+
$aggregation = [];
58+
}
59+
}
60+
61+
return $aggregation;
62+
}

src/Functional/Exceptions/InvalidArgumentException.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,17 @@ public static function assertPair($pair, $callee, $position): void
295295
}
296296
}
297297

298+
public static function assertStringable($value, $callee, $position, $positionInCollection = null): void
299+
{
300+
if (!(\is_string($value) || \is_numeric($value) || (\is_object($value) && \method_exists($value, '__toString')))) {
301+
if (!\is_null($positionInCollection)) {
302+
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)));
303+
} else {
304+
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)));
305+
}
306+
}
307+
}
308+
298309
private static function getType($value)
299310
{
300311
return \is_object($value) ? \get_class($value) : \gettype($value);

src/Functional/Functional.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ final class Functional
3333
*/
3434
const capture = '\Functional\capture';
3535

36+
/**
37+
* @see \Functional\cartesian_product
38+
*/
39+
const cartesian_product = '\Functional\cartesian_product';
40+
3641
/**
3742
* @see \Functional\compare_object_hash_on
3843
*/
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
/**
4+
* @package Functional-php
5+
* @author Hugo Sales <hugo@hsal.es>
6+
* @copyright 2021 Lars Strojny
7+
* @license https://opensource.org/licenses/MIT MIT
8+
* @link https://github.com/lstrojny/functional-php
9+
*/
10+
11+
namespace Functional\Tests;
12+
13+
use function Functional\cartesian_product;
14+
15+
class CartesianProductTest extends AbstractTestCase
16+
{
17+
public function testCartesianProduct(): void
18+
{
19+
self::assertSame(['1-one', '1-two', '2-one', '2-two'], cartesian_product('-', [1, 2], ['one', 'two']));
20+
self::assertSame(['1-one', '1_one', '1-two', '1_two', '2-one', '2_one', '2-two', '2_two'], cartesian_product(['-', '_'], [1, 2], ['one', 'two']));
21+
self::assertSame(['1one', '1two', '2one', '2two'], cartesian_product('', [1, 2], ['one', 'two']));
22+
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']));
23+
}
24+
}

tests/Functional/Exceptions/InvalidArgumentExceptionTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,34 @@ public function testAssertPairWithThreeCharacterString(): void
195195
$this->expectExceptionMessage('func() expects paramter 1 to be a pair (array with two elements)');
196196
InvalidArgumentException::assertPair('abc', "func", 1);
197197
}
198+
199+
public function testAssertStringableValid(): void
200+
{
201+
$this->expectNotToPerformAssertions();
202+
InvalidArgumentException::assertStringable('abc', "func", 1);
203+
InvalidArgumentException::assertStringable(1, "func", 1);
204+
InvalidArgumentException::assertStringable(1.2, "func", 1);
205+
InvalidArgumentException::assertStringable(1.2, "func", 1);
206+
InvalidArgumentException::assertStringable(
207+
new class
208+
{
209+
public function __toString()
210+
{
211+
}
212+
},
213+
"func",
214+
1
215+
);
216+
}
217+
218+
public function testAssertStringableInvalid(): void
219+
{
220+
$this->expectException(InvalidArgumentException::class);
221+
$this->expectExceptionMessage('func() expects paramter 1 to be a string or an object with a __toString method (got type boolean)');
222+
InvalidArgumentException::assertStringable(false, "func", 1);
223+
$this->expectExceptionMessage('func() expects paramter 1 to be a string or an object with a __toString method (got type array)');
224+
InvalidArgumentException::assertStringable([], "func", 1);
225+
$this->expectExceptionMessage('func() expects paramter 1 to be a string or an object with a __toString method (got type stdClass)');
226+
InvalidArgumentException::assertStringable(new stdClass(), "func", 1);
227+
}
198228
}

0 commit comments

Comments
 (0)