diff --git a/src/Interfaces/ArrayViewInterface.php b/src/Interfaces/ArrayViewInterface.php index 1798e55..b8c316d 100644 --- a/src/Interfaces/ArrayViewInterface.php +++ b/src/Interfaces/ArrayViewInterface.php @@ -150,6 +150,9 @@ public function map(callable $mapper): array; * @param callable(T, U, int): T $mapper Function to transform each pair of elements. * * @return array New array with transformed elements of this view. + * + * @throws ValueError if the $data is not sequential array. + * @throws SizeError if size of $data not equals to size of the view. */ public function mapWith($data, callable $mapper): array; diff --git a/src/Traits/ArrayViewOperationsTrait.php b/src/Traits/ArrayViewOperationsTrait.php new file mode 100644 index 0000000..80eb779 --- /dev/null +++ b/src/Traits/ArrayViewOperationsTrait.php @@ -0,0 +1,341 @@ +|ArrayViewInterface|ArraySelectorInterface Selector type. + */ +trait ArrayViewOperationsTrait +{ + /** + * Filters the elements in the view based on a predicate function. + * + * ##### Example + * ```php + * $source = [1, 2, 3, 4, 5, 6]; + * $view = ArrayView::toView($source); + * + * $filtered = $view->filter(fn ($x) => $x % 2 === 0); + * $filtered->toArray(); // [2, 4, 6] + * + * $filtered[':'] = [20, 40, 60]; + * $filtered->toArray(); // [20, 40, 60] + * $source; // [1, 20, 3, 40, 5, 60] + * ``` + * + * @param callable(T, int): bool $predicate Function that returns a boolean value for each element. + * + * @return ArrayMaskView A new view with elements that satisfy the predicate. + */ + public function filter(callable $predicate): ArrayViewInterface + { + return $this->is($predicate)->select($this); + } + + /** + * Checks if all elements in the view satisfy a given predicate function. + * + * ##### Example + * ```php + * $source = [1, 2, 3, 4, 5, 6]; + * $view = ArrayView::toView($source); + * + * $mask = $view->is(fn ($x) => $x % 2 === 0); + * $mask->getValue(); // [false, true, false, true, false, true] + * + * $view->subview($mask)->toArray(); // [2, 4, 6] + * $view[$mask]; // [2, 4, 6] + * + * $view[$mask] = [20, 40, 60]; + * $source; // [1, 20, 3, 40, 5, 60] + * ``` + * + * @param callable(T, int): bool $predicate Function that returns a boolean value for each element. + * + * @return MaskSelector Boolean mask for selecting elements that satisfy the predicate. + * + * @see ArrayViewInterface::match() Full synonim. + */ + public function is(callable $predicate): MaskSelectorInterface + { + $data = $this->toArray(); + return new MaskSelector(array_map($predicate, $data, array_keys($data))); + } + + /** + * Checks if all elements in the view satisfy a given predicate function. + * + * ##### Example + * ```php + * $source = [1, 2, 3, 4, 5, 6]; + * $view = ArrayView::toView($source); + * + * $mask = $view->match(fn ($x) => $x % 2 === 0); + * $mask->getValue(); // [false, true, false, true, false, true] + * + * $view->subview($mask)->toArray(); // [2, 4, 6] + * $view[$mask]; // [2, 4, 6] + * + * $view[$mask] = [20, 40, 60]; + * $source; // [1, 20, 3, 40, 5, 60] + * ``` + * + * @param callable(T, int): bool $predicate Function that returns a boolean value for each element. + * + * @return MaskSelector Boolean mask for selecting elements that satisfy the predicate. + * + * @see ArrayView::match() Full synonim. + */ + public function match(callable $predicate): MaskSelectorInterface + { + return $this->is($predicate); + } + + /** + * Compares the elements of the current ArrayView instance with another array or ArrayView + * using the provided comparator function. + * + * ##### Example + * ```php + * $source = [1, 2, 3, 4, 5, 6]; + * $view = ArrayView::toView($source); + * + * $data = [6, 5, 4, 3, 2, 1]; + * + * $mask = $view->matchWith($data, fn ($lhs, $rhs) => $lhs > $rhs); + * $mask->getValue(); // [false, false, false, true, true, true] + * + * $view->subview($mask)->toArray(); // [4, 5, 6] + * $view[$mask]; // [4, 5, 6] + * + * $view[$mask] = [40, 50, 60]; + * $source; // [1, 2, 3, 40, 50, 60] + * ``` + * + * @template U The type of the elements in the array for comparison with. + * + * @param array|ArrayViewInterface|U $data The array or ArrayView to compare to. + * @param callable(T, U, int): bool $comparator Function that determines the comparison logic between the elements. + * + * @return MaskSelectorInterface A MaskSelector instance representing the results of the element comparisons. + * + * @throws ValueError if the $data is not sequential array. + * @throws SizeError if size of $data not equals to size of the view. + * + * @see ArrayView::is() Full synonim. + */ + public function matchWith($data, callable $comparator): MaskSelectorInterface + { + $data = $this->checkAndConvertArgument($data); + return new MaskSelector(array_map($comparator, $this->toArray(), $data, array_keys($data))); + } + + /** + * Transforms each element of the array using the given callback function. + * + * The callback function receives two parameters: the current element of the array and its index. + * + * ##### Example + * ```php + * $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + * $subview = ArrayView::toView($source)->subview('::2'); // [1, 3, 5, 7, 9] + * + * $subview->map(fn ($x) => $x * 10); // [10, 30, 50, 70, 90] + * ``` + * + * @param callable(T, int): T $mapper Function to transform each element. + * + * @return array New array with transformed elements of this view. + */ + public function map(callable $mapper): array + { + $result = []; + $size = \count($this); + for ($i = 0; $i < $size; $i++) { + /** @var T $item */ + $item = $this[$i]; + $result[$i] = $mapper($item, $i); + } + return $result; + } + + /** + * Transforms each pair of elements from the current array view and the provided data array using the given + * callback function. + * + * The callback function receives three parameters: the current element of the current array view, + * the corresponding element of the data array, and the index. + * + * ##### Example + * ```php + * $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + * $subview = ArrayView::toView($source)->subview('::2'); // [1, 3, 5, 7, 9] + * + * $data = [9, 27, 45, 63, 81]; + * + * $subview->mapWith($data, fn ($lhs, $rhs) => $lhs * $rhs); // [10, 30, 50, 70, 90] + * ``` + * + * @template U The type rhs of a binary operation. + * + * @param array|ArrayViewInterface|U $data The rhs values for a binary operation. + * @param callable(T, U, int): T $mapper Function to transform each pair of elements. + * + * @return array New array with transformed elements of this view. + * + * @throws ValueError if the $data is not sequential array. + * @throws SizeError if size of $data not equals to size of the view. + */ + public function mapWith($data, callable $mapper): array + { + $data = $this->checkAndConvertArgument($data); + $result = []; + + $size = \count($this); + for ($i = 0; $i < $size; $i++) { + /** @var T $lhs */ + $lhs = $this[$i]; + /** @var U $rhs */ + $rhs = $data[$i]; + $result[$i] = $mapper($lhs, $rhs, $i); + } + + return $result; + } + + /** + * Applies a transformation function to each element in the view. + * + * ##### Example + * ```php + * $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + * $subview = ArrayView::toView($source)->subview('::2'); // [1, 3, 5, 7, 9] + * + * $subview->apply(fn ($x) => $x * 10); + * + * $subview->toArray(); // [10, 30, 50, 70, 90] + * $source; // [10, 2, 30, 4, 50, 6, 70, 8, 90, 10] + * ``` + * + * @param callable(T, int): T $mapper Function to transform each element. + * + * @return ArrayView this view. + */ + public function apply(callable $mapper): self + { + $size = \count($this); + for ($i = 0; $i < $size; $i++) { + /** @var T $item */ + $item = $this[$i]; + $this[$i] = $mapper($item, $i); + } + return $this; + } + + /** + * Sets new values for the elements in the view. + * + * ##### Example + * ```php + * $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + * $subview = ArrayView::toView($source)->subview('::2'); // [1, 3, 5, 7, 9] + * + * $data = [9, 27, 45, 63, 81]; + * + * $subview->applyWith($data, fn ($lhs, $rhs) => $lhs * $rhs); + * $subview->toArray(); // [10, 30, 50, 70, 90] + * + * $source; // [10, 2, 30, 4, 50, 6, 70, 8, 90, 10] + * ``` + * + * @template U Type of $data items. + * + * @param array|ArrayViewInterface $data + * @param callable(T, U, int): T $mapper + * + * @return ArrayView this view. + * + * @throws ValueError if the $data is not sequential array. + * @throws SizeError if size of $data not equals to size of the view. + */ + public function applyWith($data, callable $mapper): self + { + $data = $this->checkAndConvertArgument($data); + + $size = \count($this); + for ($i = 0; $i < $size; $i++) { + /** @var T $lhs */ + $lhs = $this[$i]; + /** @var U $rhs */ + $rhs = $data[$i]; + $this[$i] = $mapper($lhs, $rhs, $i); + } + + return $this; + } + + /** + * Check if the given source array is sequential (indexed from 0 to n-1). + * + * If the array is not sequential, a ValueError is thrown indicating that + * a view cannot be created for a non-sequential array. + * + * @param mixed $source The source array to check for sequential indexing. + * + * @return void + * + * @throws ValueError if the source array is not sequential. + */ + protected function checkSequentialArgument($source): void + { + if ($source instanceof ArrayViewInterface) { + return; + } + + if (\is_array($source) && !Util::isArraySequential($source)) { + throw new ValueError('Argument is not sequential.'); + } + } + + /** + * Util function for checking and converting data argument. + * + * @template U Type of $data items. + * + * @param array|ArrayViewInterface|U $data The rhs values for a binary operation. + * + * @return array converted data. + */ + protected function checkAndConvertArgument($data): array + { + $this->checkSequentialArgument($data); + + if ($data instanceof ArrayViewInterface) { + $data = $data->toArray(); + } elseif (!\is_array($data)) { + $data = \array_fill(0, \count($this), $data); + } + + [$dataSize, $thisSize] = [\count($data), \count($this)]; + if ($dataSize !== $thisSize) { + throw new SizeError("Length of values array not equal to view length ({$dataSize} != {$thisSize})."); + } + + return $data; + } +} diff --git a/src/Views/ArrayView.php b/src/Views/ArrayView.php index cb0b829..5c1e692 100644 --- a/src/Views/ArrayView.php +++ b/src/Views/ArrayView.php @@ -11,9 +11,8 @@ use Smoren\ArrayView\Exceptions\ValueError; use Smoren\ArrayView\Interfaces\ArraySelectorInterface; use Smoren\ArrayView\Interfaces\ArrayViewInterface; -use Smoren\ArrayView\Interfaces\MaskSelectorInterface; -use Smoren\ArrayView\Selectors\MaskSelector; use Smoren\ArrayView\Traits\ArrayViewAccessTrait; +use Smoren\ArrayView\Traits\ArrayViewOperationsTrait; use Smoren\ArrayView\Util; /** @@ -37,6 +36,12 @@ class ArrayView implements ArrayViewInterface * for array access methods. */ use ArrayViewAccessTrait; + /** + * @use ArrayViewOperationsTrait|ArrayViewInterface|ArraySelectorInterface> + * + * for utils methods. + */ + use ArrayViewOperationsTrait; /** * @var array|ArrayViewInterface The source array or view. @@ -152,7 +157,7 @@ public static function toUnlinkedView($source, ?bool $readonly = null): ArrayVie */ public function __construct(&$source, ?bool $readonly = null) { - $this->checkSequential($source); + $this->checkSequentialArgument($source); $this->source = &$source; $this->readonly = $readonly ?? (($source instanceof ArrayViewInterface) ? $source->isReadonly() : false); @@ -237,268 +242,6 @@ public function subview($selector, bool $readonly = null): ArrayViewInterface return $this->toSelector($selector)->select($this, $readonly); } - /** - * Filters the elements in the view based on a predicate function. - * - * ##### Example - * ```php - * $source = [1, 2, 3, 4, 5, 6]; - * $view = ArrayView::toView($source); - * - * $filtered = $view->filter(fn ($x) => $x % 2 === 0); - * $filtered->toArray(); // [2, 4, 6] - * - * $filtered[':'] = [20, 40, 60]; - * $filtered->toArray(); // [20, 40, 60] - * $source; // [1, 20, 3, 40, 5, 60] - * ``` - * - * @param callable(T, int): bool $predicate Function that returns a boolean value for each element. - * - * @return ArrayMaskView A new view with elements that satisfy the predicate. - */ - public function filter(callable $predicate): ArrayViewInterface - { - return $this->is($predicate)->select($this); - } - - /** - * Checks if all elements in the view satisfy a given predicate function. - * - * ##### Example - * ```php - * $source = [1, 2, 3, 4, 5, 6]; - * $view = ArrayView::toView($source); - * - * $mask = $view->is(fn ($x) => $x % 2 === 0); - * $mask->getValue(); // [false, true, false, true, false, true] - * - * $view->subview($mask)->toArray(); // [2, 4, 6] - * $view[$mask]; // [2, 4, 6] - * - * $view[$mask] = [20, 40, 60]; - * $source; // [1, 20, 3, 40, 5, 60] - * ``` - * - * @param callable(T, int): bool $predicate Function that returns a boolean value for each element. - * - * @return MaskSelector Boolean mask for selecting elements that satisfy the predicate. - * - * @see ArrayViewInterface::match() Full synonim. - */ - public function is(callable $predicate): MaskSelectorInterface - { - $data = $this->toArray(); - return new MaskSelector(array_map($predicate, $data, array_keys($data))); - } - - /** - * Checks if all elements in the view satisfy a given predicate function. - * - * ##### Example - * ```php - * $source = [1, 2, 3, 4, 5, 6]; - * $view = ArrayView::toView($source); - * - * $mask = $view->is(fn ($x) => $x % 2 === 0); - * $mask->getValue(); // [false, true, false, true, false, true] - * - * $view->subview($mask)->toArray(); // [2, 4, 6] - * $view[$mask]; // [2, 4, 6] - * - * $view[$mask] = [20, 40, 60]; - * $source; // [1, 20, 3, 40, 5, 60] - * ``` - * - * @param callable(T, int): bool $predicate Function that returns a boolean value for each element. - * - * @return MaskSelector Boolean mask for selecting elements that satisfy the predicate. - * - * @see ArrayView::match() Full synonim. - */ - public function match(callable $predicate): MaskSelectorInterface - { - return $this->is($predicate); - } - - /** - * Compares the elements of the current ArrayView instance with another array or ArrayView - * using the provided comparator function. - * - * @template U The type of the elements in the array for comparison with. - * - * @param array|ArrayViewInterface|U $data The array or ArrayView to compare to. - * @param callable(T, U, int): bool $comparator Function that determines the comparison logic between the elements. - * - * @return MaskSelectorInterface A MaskSelector instance representing the results of the element comparisons. - * - * @throws ValueError if the $data is not sequential array. - * @throws SizeError if size of $data not equals to size of the view. - * - * @see ArrayView::is() Full synonim. - */ - public function matchWith($data, callable $comparator): MaskSelectorInterface - { - $this->checkSequential($data); // TODO test - - if ($data instanceof ArrayViewInterface) { - $data = $data->toArray(); - } elseif (!\is_array($data)) { - $data = \array_fill(0, \count($this), $data); - } - - [$dataSize, $thisSize] = [\count($data), \count($this)]; - if ($dataSize !== $thisSize) { - throw new SizeError("Length of values array not equal to view length ({$dataSize} != {$thisSize})."); - } - - $data = $data instanceof ArrayViewInterface ? $data->toArray() : $data; - return new MaskSelector(array_map($comparator, $this->toArray(), $data, array_keys($data))); - } - - /** - * Transforms each element of the array using the given callback function. - * - * The callback function receives two parameters: the current element of the array and its index. - * - * @param callable(T, int): T $mapper Function to transform each element. - * - * @return array New array with transformed elements of this view. - */ - public function map(callable $mapper): array - { - $result = []; - $size = \count($this); - for ($i = 0; $i < $size; $i++) { - /** @var T $item */ - $item = $this[$i]; - $result[$i] = $mapper($item, $i); - } - return $result; - } - - /** - * Transforms each pair of elements from the current array view and the provided data array using the given - * callback function. - * - * The callback function receives three parameters: the current element of the current array view, - * the corresponding element of the data array, and the index. - * - * @template U The type rhs of a binary operation. - * - * @param array|ArrayViewInterface|U $data The rhs values for a binary operation. - * @param callable(T, U, int): T $mapper Function to transform each pair of elements. - * - * @return array New array with transformed elements of this view. - */ - public function mapWith($data, callable $mapper): array - { - $this->checkSequential($data); - - if ($data instanceof ArrayViewInterface) { - $data = $data->toArray(); - } elseif (!\is_array($data)) { - $data = \array_fill(0, \count($this), $data); - } - - [$dataSize, $thisSize] = [\count($data), \count($this)]; - if ($dataSize !== $thisSize) { - throw new SizeError("Length of values array not equal to view length ({$dataSize} != {$thisSize})."); - } - - $dataView = ArrayView::toView($data); - $result = []; - - $size = \count($this); - for ($i = 0; $i < $size; $i++) { - /** @var T $lhs */ - $lhs = $this[$i]; - /** @var U $rhs */ - $rhs = $dataView[$i]; - $result[$i] = $mapper($lhs, $rhs, $i); - } - - return $result; - } - - /** - * Applies a transformation function to each element in the view. - * - * ##### Example - * ```php - * $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - * $subview = ArrayView::toView($source)->subview('::2'); // [1, 3, 5, 7, 9] - * - * $subview->apply(fn ($x) => $x * 10); - * - * $subview->toArray(); // [10, 30, 50, 70, 90] - * $source; // [10, 2, 30, 4, 50, 6, 70, 8, 90, 10] - * ``` - * - * @param callable(T, int): T $mapper Function to transform each element. - * - * @return ArrayView this view. - */ - public function apply(callable $mapper): self - { - $size = \count($this); - for ($i = 0; $i < $size; $i++) { - /** @var T $item */ - $item = $this[$i]; - $this[$i] = $mapper($item, $i); - } - return $this; - } - - /** - * Sets new values for the elements in the view. - * - * ##### Example - * ```php - * $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - * $subview = ArrayView::toView($source)->subview('::2'); // [1, 3, 5, 7, 9] - * - * $data = [9, 27, 45, 63, 81]; - * - * $subview->applyWith($data, fn ($lhs, $rhs) => $lhs * $rhs); - * $subview->toArray(); // [10, 30, 50, 70, 90] - * - * $source; // [10, 2, 30, 4, 50, 6, 70, 8, 90, 10] - * ``` - * - * @template U Type of $data items. - * - * @param array|ArrayViewInterface $data - * @param callable(T, U, int): T $mapper - * - * @return ArrayView this view. - * - * @throws ValueError if the $data is not sequential array. - * @throws SizeError if size of $data not equals to size of the view. - */ - public function applyWith($data, callable $mapper): self - { - $this->checkSequential($data); - - [$dataSize, $thisSize] = [\count($data), \count($this)]; - if ($dataSize !== $thisSize) { - throw new SizeError("Length of values array not equal to view length ({$dataSize} != {$thisSize})."); - } - - $dataView = ArrayView::toView($data); - - $size = \count($this); - for ($i = 0; $i < $size; $i++) { - /** @var T $lhs */ - $lhs = $this[$i]; - /** @var U $rhs */ - $rhs = $dataView[$i]; - $this[$i] = $mapper($lhs, $rhs, $i); - } - - return $this; - } - /** * Sets new values for the elements in the view. * @@ -522,7 +265,7 @@ public function applyWith($data, callable $mapper): self */ public function set($newValues): self { - $this->checkSequential($newValues); + $this->checkSequentialArgument($newValues); if (!\is_array($newValues) && !($newValues instanceof ArrayViewInterface)) { $size = \count($this); @@ -630,29 +373,6 @@ protected function getParentSize(): int : \count($this->source); } - /** - * Check if the given source array is sequential (indexed from 0 to n-1). - * - * If the array is not sequential, a ValueError is thrown indicating that - * a view cannot be created for a non-sequential array. - * - * @param mixed $source The source array to check for sequential indexing. - * - * @return void - * - * @throws ValueError if the source array is not sequential. - */ - protected function checkSequential($source): void - { - if ($source instanceof ArrayViewInterface) { - return; - } - - if (\is_array($source) && !Util::isArraySequential($source)) { - throw new ValueError('Cannot create view for non-sequential array.'); - } - } - /** * Convert the given index to a valid index within the source array. * diff --git a/tests/unit/ArrayView/ErrorsTest.php b/tests/unit/ArrayView/ErrorsTest.php index 3023e0d..20fd41d 100644 --- a/tests/unit/ArrayView/ErrorsTest.php +++ b/tests/unit/ArrayView/ErrorsTest.php @@ -156,22 +156,6 @@ public function testInvalidSliceWrite(array $source, string $slice) $view[new SliceSelector($slice)] = [1, 2, 3]; } - /** - * @dataProvider dataProviderForApplyWithSizeError - */ - public function testApplyWithSizeError(array $source, callable $viewGetter, callable $mapper, array $toApplyWith) - { - $view = ArrayView::toView($source); - - $sourceSize = \count($source); - $argSize = \count($toApplyWith); - - $this->expectException(SizeError::class); - $this->expectExceptionMessage("Length of values array not equal to view length ({$argSize} != {$sourceSize})."); - - $view->applyWith($toApplyWith, $mapper); - } - /** * @dataProvider dataProviderForWriteSizeError */ @@ -237,6 +221,102 @@ public function testWriteBadIndexList(array $source, array $indexes) } } + /** + * @dataProvider dataProviderForSequentialError + */ + public function testMapWithSequentialError(array $source, array $arg) + { + $view = ArrayView::toView($source); + + try { + $view->mapWith($arg, fn ($lhs, $rhs) => $lhs + $rhs); + } catch (ValueError $e) { + $this->assertSame('Argument is not sequential.', $e->getMessage()); + } + } + + /** + * @dataProvider dataProviderForSizeError + */ + public function testMapWithSizeError(array $source, array $arg) + { + $view = ArrayView::toView($source); + + try { + $view->mapWith($arg, fn ($lhs, $rhs) => $lhs + $rhs); + } catch (SizeError $e) { + [$lhsSize, $rhsSize] = array_map('count', [$arg, $source]); + $this->assertSame( + "Length of values array not equal to view length ({$lhsSize} != {$rhsSize}).", + $e->getMessage() + ); + } + } + + /** + * @dataProvider dataProviderForSequentialError + */ + public function testMatchWithSequentialError(array $source, array $arg) + { + $view = ArrayView::toView($source); + + try { + $view->matchWith($arg, fn ($lhs, $rhs) => $lhs && $rhs); + } catch (ValueError $e) { + $this->assertSame('Argument is not sequential.', $e->getMessage()); + } + } + + /** + * @dataProvider dataProviderForSizeError + */ + public function testMatchWithSizeError(array $source, array $arg) + { + $view = ArrayView::toView($source); + + try { + $view->matchWith($arg, fn ($lhs, $rhs) => $lhs && $rhs); + } catch (SizeError $e) { + [$lhsSize, $rhsSize] = array_map('count', [$arg, $source]); + $this->assertSame( + "Length of values array not equal to view length ({$lhsSize} != {$rhsSize}).", + $e->getMessage() + ); + } + } + + /** + * @dataProvider dataProviderForSequentialError + */ + public function testApplyWithSequentialError(array $source, array $arg) + { + $view = ArrayView::toView($source); + + try { + $view->applyWith($arg, fn ($lhs, $rhs) => $lhs + $rhs); + } catch (ValueError $e) { + $this->assertSame('Argument is not sequential.', $e->getMessage()); + } + } + + /** + * @dataProvider dataProviderForSizeError + */ + public function testApplyWithSizeError(array $source, array $arg) + { + $view = ArrayView::toView($source); + + try { + $view->applyWith($arg, fn ($lhs, $rhs) => $lhs && $rhs); + } catch (SizeError $e) { + [$lhsSize, $rhsSize] = array_map('count', [$arg, $source]); + $this->assertSame( + "Length of values array not equal to view length ({$lhsSize} != {$rhsSize}).", + $e->getMessage() + ); + } + } + public function dataProviderForOutOfRangeIndexes(): array { return [ @@ -301,42 +381,6 @@ public function dataProviderForInvalidSlice(): array ]; } - public function dataProviderForApplyWithSizeError(): array - { - return [ - [ - [], - fn (array &$source) => ArrayView::toView($source), - fn (int $item) => $item, - [1], - ], - [ - [1], - fn (array &$source) => ArrayView::toView($source), - fn (int $item) => $item, - [], - ], - [ - [1], - fn (array &$source) => ArrayView::toView($source), - fn (int $item) => $item, - [1, 2], - ], - [ - [1, 2, 3], - fn (array &$source) => ArrayView::toView($source), - fn (int $item) => $item, - [1, 2], - ], - [ - [1, 2, 3], - fn (array &$source) => ArrayView::toView($source), - fn (int $item) => $item, - [1, 2, 3, 4, 5], - ], - ]; - } - public function dataProviderForWriteSizeError(): array { return [ @@ -465,4 +509,27 @@ public function dataProviderForNonSequentialError(): array }], ]; } + + public function dataProviderForSequentialError(): array + { + return [ + [[], ['test' => 123]], + [[], [1 => 1]], + [[], [0, 2 => 1]], + [[1, 2, 3], ['test' => 123]], + [[1, 2, 3], [1 => 1]], + [[1, 2, 3], [0, 2 => 1]], + ]; + } + + public function dataProviderForSizeError(): array + { + return [ + [[], [1]], + [[], [1, 2, 3]], + [[1, 2, 3], []], + [[1, 2, 3], [1, 2]], + [[1, 2, 3], [1, 2, 3, 4]], + ]; + } } diff --git a/tests/unit/ArrayView/ReadTest.php b/tests/unit/ArrayView/ReadTest.php index bd14296..6e90cb3 100644 --- a/tests/unit/ArrayView/ReadTest.php +++ b/tests/unit/ArrayView/ReadTest.php @@ -502,12 +502,19 @@ public function dataProviderForMatchWith(): array [true, false, true, true, true, true, true, true, false, true], [1, 3, 4, 5, 6, 7, 8, 10], ], + [ + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + ArrayView::toUnlinkedView([1, 22, 3, 4, 5, 6, 7, 8, 99, 10]), + fn (int $lhs, int $rhs) => $lhs >= $rhs, + [true, false, true, true, true, true, true, true, false, true], + [1, 3, 4, 5, 6, 7, 8, 10], + ], [ [1, 2, 3], 1, fn (int $lhs, int $rhs) => $lhs > $rhs, [false, true, true], - [1, 3, 4, 5, 6, 7, 8, 10], + [2, 3], ], ]; } diff --git a/tests/unit/ArrayView/WriteTest.php b/tests/unit/ArrayView/WriteTest.php index 6ff8df7..4e50a20 100644 --- a/tests/unit/ArrayView/WriteTest.php +++ b/tests/unit/ArrayView/WriteTest.php @@ -149,7 +149,7 @@ public function testApply(array $source, callable $viewGetter, callable $mapper, /** * @dataProvider dataProviderForApplyWith */ - public function testApplyWith(array $source, callable $viewGetter, callable $mapper, array $arg, array $expected) + public function testApplyWith(array $source, callable $viewGetter, callable $mapper, $arg, array $expected) { // Given $view = $viewGetter($source); @@ -519,6 +519,13 @@ public function dataProviderForApplyWith(): array [1, 2, 3, 4, 5], [1, 2, 6, 4, 15, 6, 28, 8, 45, 10], ], + [ + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + fn (array &$source) => ArrayView::toView($source)->subview('::2'), + fn (int $lhs, int $rhs) => $lhs * $rhs, + 10, + [10, 2, 30, 4, 50, 6, 70, 8, 90, 10], + ], ]; } }