Skip to content

Commit

Permalink
In progress...
Browse files Browse the repository at this point in the history
  • Loading branch information
Smoren committed Mar 19, 2024
1 parent 22fb3d5 commit 3a3dcba
Show file tree
Hide file tree
Showing 3 changed files with 288 additions and 56 deletions.
45 changes: 36 additions & 9 deletions src/Interfaces/ArrayViewInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,20 @@ public static function toUnlinkedView($source, ?bool $readonly = null): ArrayVie
*/
public function toArray(): array;

/**
* Returns a subview of this view based on a selector or string slice.
*
* @param string|array<int|bool>|ArrayViewInterface<int|bool>|ArraySelectorInterface $selector The selector or
* string to filter the subview.
* @param bool|null $readonly Flag indicating if the subview should be read-only.
*
* @return ArrayViewInterface<T> A new view representing the subview of this view.
*
* @throws IndexError if the selector is IndexListSelector and some indexes are out of range.
* @throws SizeError if the selector is MaskSelector and size of the mask not equals to size of the view.
*/
public function subview($selector, bool $readonly = null): ArrayViewInterface;

/**
* Filters the elements in the view based on a predicate function.
*
Expand Down Expand Up @@ -102,7 +116,7 @@ public function match(callable $predicate): MaskSelectorInterface;
*
* @template U The type of the elements in the array for comparison with.
*
* @param array<U>|ArrayViewInterface<U> $data The array or ArrayView to compare to.
* @param array<U>|ArrayViewInterface<U>|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.
Expand All @@ -113,18 +127,31 @@ public function match(callable $predicate): MaskSelectorInterface;
public function matchWith($data, callable $comparator): MaskSelectorInterface;

/**
* Returns a subview of this view based on a selector or string slice.
* Transforms each element of the array using the given callback function.
*
* @param string|array<int|bool>|ArrayViewInterface<int|bool>|ArraySelectorInterface $selector The selector or
* string to filter the subview.
* @param bool|null $readonly Flag indicating if the subview should be read-only.
* The callback function receives two parameters: the current element of the array and its index.
*
* @return ArrayViewInterface<T> A new view representing the subview of this view.
* @param callable(T, int): T $mapper Function to transform each element.
*
* @throws IndexError if the selector is IndexListSelector and some indexes are out of range.
* @throws SizeError if the selector is MaskSelector and size of the mask not equals to size of the view.
* @return array<T> New array with transformed elements of this view.
*/
public function subview($selector, bool $readonly = null): ArrayViewInterface;
public function map(callable $mapper): array;

/**
* 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<U>|ArrayViewInterface<U>|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<mixed> New array with transformed elements of this view.
*/
public function mapWith($data, callable $mapper): array;

/**
* Applies a transformation function to each element in the view.
Expand Down
176 changes: 129 additions & 47 deletions src/Views/ArrayView.php
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,63 @@ public function toArray(): array
return [...$this];
}

/**
* Returns a subview of this view based on a selector or string slice.
*
* ##### Example (using selector objects)
* ```php
* $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
*
* $subview = ArrayView::toView($source)
* ->subview(new SliceSelector('::2')) // [1, 3, 5, 7, 9]
* ->subview(new MaskSelector([true, false, true, true, true])) // [1, 5, 7, 9]
* ->subview(new IndexListSelector([0, 1, 2])) // [1, 5, 7]
* ->subview(new SliceSelector('1:')); // [5, 7]
*
* $subview[':'] = [55, 77];
* print_r($source); // [1, 2, 3, 4, 55, 6, 77, 8, 9, 10]
* ```
*
* ##### Example (using short objects)
* ```php
* $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
*
* $subview = ArrayView::toView($source)
* ->subview('::2') // [1, 3, 5, 7, 9]
* ->subview([true, false, true, true, true]) // [1, 5, 7, 9]
* ->subview([0, 1, 2]) // [1, 5, 7]
* ->subview('1:'); // [5, 7]
*
* $subview[':'] = [55, 77];
* print_r($source); // [1, 2, 3, 4, 55, 6, 77, 8, 9, 10]
* ```
*
* ##### Readonly example
* ```php
* $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
* $subview = ArrayView::toView($source)->subview('::2');
*
* $subview[':']; // [1, 3, 5, 7, 9]
* $subview[':'] = [11, 33, 55, 77, 99]; // throws ReadonlyError
* $subview[0] = [11]; // throws ReadonlyError
* ```
*
* @template S of string|array<int|bool>|ArrayViewInterface<int|bool>|ArraySelectorInterface Selector type.
*
* @param S $selector The selector or string to filter the subview.
* @param bool|null $readonly Flag indicating if the subview should be read-only.
*
* @return ArrayViewInterface<T> A new view representing the subview of this view.
*
* @throws IndexError if the selector is IndexListSelector and some indexes are out of range.
* @throws SizeError if the selector is MaskSelector and size of the mask not equals to size of the view.
* @throws KeyError if the selector is not valid (e.g. non-sequential array).
*/
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.
*
Expand Down Expand Up @@ -270,7 +327,7 @@ public function match(callable $predicate): MaskSelectorInterface
*
* @template U The type of the elements in the array for comparison with.
*
* @param array<U>|ArrayViewInterface<U> $data The array or ArrayView to compare to.
* @param array<U>|ArrayViewInterface<U>|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.
Expand All @@ -282,65 +339,86 @@ public function match(callable $predicate): MaskSelectorInterface
*/
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;

Check failure on line 355 in src/Views/ArrayView.php

View workflow job for this annotation

GitHub Actions / Test (7.4)

Instanceof between array and Smoren\ArrayView\Interfaces\ArrayViewInterface will always evaluate to false.

Check failure on line 355 in src/Views/ArrayView.php

View workflow job for this annotation

GitHub Actions / Test (8.0)

Instanceof between array and Smoren\ArrayView\Interfaces\ArrayViewInterface will always evaluate to false.

Check failure on line 355 in src/Views/ArrayView.php

View workflow job for this annotation

GitHub Actions / Test (8.1)

Instanceof between array and Smoren\ArrayView\Interfaces\ArrayViewInterface will always evaluate to false.

Check failure on line 355 in src/Views/ArrayView.php

View workflow job for this annotation

GitHub Actions / Test (8.2)

Instanceof between array and Smoren\ArrayView\Interfaces\ArrayViewInterface will always evaluate to false.

Check failure on line 355 in src/Views/ArrayView.php

View workflow job for this annotation

GitHub Actions / Test (8.3)

Instanceof between array and Smoren\ArrayView\Interfaces\ArrayViewInterface will always evaluate to false.
return new MaskSelector(array_map($comparator, $this->toArray(), $data, array_keys($data)));
}

/**
* Returns a subview of this view based on a selector or string slice.
*
* ##### Example (using selector objects)
* ```php
* $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
*
* $subview = ArrayView::toView($source)
* ->subview(new SliceSelector('::2')) // [1, 3, 5, 7, 9]
* ->subview(new MaskSelector([true, false, true, true, true])) // [1, 5, 7, 9]
* ->subview(new IndexListSelector([0, 1, 2])) // [1, 5, 7]
* ->subview(new SliceSelector('1:')); // [5, 7]
*
* $subview[':'] = [55, 77];
* print_r($source); // [1, 2, 3, 4, 55, 6, 77, 8, 9, 10]
* ```
*
* ##### Example (using short objects)
* ```php
* $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
*
* $subview = ArrayView::toView($source)
* ->subview('::2') // [1, 3, 5, 7, 9]
* ->subview([true, false, true, true, true]) // [1, 5, 7, 9]
* ->subview([0, 1, 2]) // [1, 5, 7]
* ->subview('1:'); // [5, 7]
* Transforms each element of the array using the given callback function.
*
* $subview[':'] = [55, 77];
* print_r($source); // [1, 2, 3, 4, 55, 6, 77, 8, 9, 10]
* ```
* The callback function receives two parameters: the current element of the array and its index.
*
* ##### Readonly example
* ```php
* $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
* $subview = ArrayView::toView($source)->subview('::2');
* @param callable(T, int): T $mapper Function to transform each element.
*
* $subview[':']; // [1, 3, 5, 7, 9]
* $subview[':'] = [11, 33, 55, 77, 99]; // throws ReadonlyError
* $subview[0] = [11]; // throws ReadonlyError
* ```
* @return array<T> 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.
*
* @template S of string|array<int|bool>|ArrayViewInterface<int|bool>|ArraySelectorInterface Selector type.
* The callback function receives three parameters: the current element of the current array view,
* the corresponding element of the data array, and the index.
*
* @param S $selector The selector or string to filter the subview.
* @param bool|null $readonly Flag indicating if the subview should be read-only.
* @template U The type rhs of a binary operation.
*
* @return ArrayViewInterface<T> A new view representing the subview of this view.
* @param array<U>|ArrayViewInterface<U>|U $data The rhs values for a binary operation.
* @param callable(T, U, int): T $mapper Function to transform each pair of elements.
*
* @throws IndexError if the selector is IndexListSelector and some indexes are out of range.
* @throws SizeError if the selector is MaskSelector and size of the mask not equals to size of the view.
* @throws KeyError if the selector is not valid (e.g. non-sequential array).
* @return array<mixed> New array with transformed elements of this view.
*/
public function subview($selector, bool $readonly = null): ArrayViewInterface
public function mapWith($data, callable $mapper): array
{
return $this->toSelector($selector)->select($this, $readonly);
$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;
}

/**
Expand Down Expand Up @@ -566,7 +644,11 @@ protected function getParentSize(): int
*/
protected function checkSequential($source): void
{
if (is_array($source) && !Util::isArraySequential($source)) {
if ($source instanceof ArrayViewInterface) {
return;
}

if (\is_array($source) && !Util::isArraySequential($source)) {
throw new ValueError('Cannot create view for non-sequential array.');
}
}
Expand Down
Loading

0 comments on commit 3a3dcba

Please sign in to comment.