Skip to content

Commit

Permalink
Introduce OrderedListInterface::fill
Browse files Browse the repository at this point in the history
Signed-off-by: Maximilian Bösing <[email protected]>
  • Loading branch information
boesing committed Oct 17, 2020
1 parent d935782 commit e7ad3e0
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 0 deletions.
47 changes: 47 additions & 0 deletions src/OrderedList.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@

use Webmozart\Assert\Assert;
use function array_combine;
use function array_fill;
use function array_keys;
use function array_map;
use function array_merge;
use function array_replace;
use function array_values;
use function is_callable;
use function serialize;
use function sort;
use const SORT_NATURAL;
Expand Down Expand Up @@ -183,4 +186,48 @@ public function unify(

return $instance;
}

public function fill(int $startIndex, int $amount, $value): OrderedListInterface
{
Assert::greaterThanEq($startIndex, 0, 'Given $startIndex must be greater than or equal to %2$s. Got: %s');
Assert::greaterThanEq($amount, 1, 'Given $amount must be greater than or equal to %2$s. Got: %s');
Assert::lessThanEq(
$startIndex,
$this->count(),
'Give $startIndex must be less than or equal to %2$s to keep the list a continious list. Got: %s.'
);

$instance = clone $this;

/** @psalm-var list<TValue> $combined */
$combined = array_replace(
$this->data,
$this->createListFilledWithValues($startIndex, $amount, $value)
);

$instance->data = $combined;

return $instance;
}

/**
* @psalm-param TValue|Closure(int $index):TValue $value
*
* @psalm-return array<int,TValue>
*/
private function createListFilledWithValues(int $start, int $amount, $value): array
{
if (!is_callable($value)) {
/** @psalm-var array<int,TValue> $list */
$list = array_fill($start, $amount, $value);
return $list;
}

$list = [];
for ($index = $start; $index <= $amount; $index++) {
$list[$index] = $value($index);
}

return $list;
}
}
10 changes: 10 additions & 0 deletions src/OrderedListInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

namespace Boesing\TypedArrays;

use InvalidArgumentException;

/**
* @template TValue
* @template-extends ArrayInterface<int,TValue>
Expand Down Expand Up @@ -91,4 +93,12 @@ public function unify(
?callable $unificationIdentifierGenerator = null,
?callable $callback = null
): OrderedListInterface;

/**
* @psalm-param TValue|Closure(int $index):TValue $value
* @psalm-return OrderedListInterface<TValue>
* @psalm-immutable
* @throws InvalidArgumentException if start index does is not fitting in the current list state.
*/
public function fill(int $startIndex, int $amount, $value): OrderedListInterface;
}
133 changes: 133 additions & 0 deletions tests/GenericOrderedListTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
use PHPUnit\Framework\TestCase;
use stdClass;
use Webmozart\Assert\Assert;
use function array_fill;
use function chr;
use function md5;
use function mt_rand;
use function spl_object_hash;
use function strnatcmp;

Expand Down Expand Up @@ -701,4 +703,135 @@ public function testToMapConversionErrorsOnIntegerishKeys(): void
return (string) $value;
});
}

/**
* @template TValue
* @psalm-param list<TValue> $initial
* @psalm-param TValue $fillUp
* @dataProvider invalidStartIndices
*/
public function testFillWillThrowExceptionWhenStartIndexIsInvalid(
int $startIndex,
array $initial,
$fillUp,
string $expectedExceptionMessage
): void {
$list = new GenericOrderedList($initial);
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage($expectedExceptionMessage);
$list->fill($startIndex, mt_rand(1, 10), $fillUp);
}

/**
* @template TValue
* @psalm-param TValue $value
* @dataProvider scalarFillValues
*/
public function testFillAppendsScalarValues(int $amount, $value): void
{
self::assertIsScalar($value);
/** @var OrderedListInterface<TValue> $list */
$list = new GenericOrderedList([]);
$list = $list->fill(0, $amount, $value);
self::assertEquals(array_fill(0, $amount, $value), $list->toNativeArray());
}

/**
* @template mixed
* @psalm-return Generator<string,array{0:int,1:list<mixed>,2:mixed,3:non-empty-string}>
*/
public function invalidStartIndices(): Generator
{
yield 'negative' => [
-1,
[],
0,
'Given $startIndex must be greater than or equal to',
];

yield 'non continues index' => [
1,
[],
0,
'to keep the list a continious list.',
];

yield 'non continues index #2' => [
10,
[0, 1, 2,],
3,
'to keep the list a continious list.',
];
}

/**
* @psalm-return Generator<string,array{0:int,1:mixed}>
*/
public function scalarFillValues(): Generator
{
yield 'int' => [
1,
0,
];

yield 'string' => [
99,
'foo',
];

yield 'float' => [
10,
0.1,
];

yield 'true' => [
8,
true,
];

yield 'false' => [
50,
false,
];
}

public function testFillUsesCallbackToGenerateValue(): void
{
$callback = static function (int $index): string {
return chr($index + 65);
};

/** @var OrderedListInterface<string> $list */
$abc = new GenericOrderedList([]);
$abc = $abc->fill(0, 25, $callback);

self::assertEquals([
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z',
], $abc->toNativeArray());
}
}

0 comments on commit e7ad3e0

Please sign in to comment.