diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 0000000..4d8b100 --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,19 @@ +engines: + phpcodesniffer: + enabled: true + checks: + Generic WhiteSpace ScopeIndent IncorrectExact: + enabled: false + phpmd: + enabled: true + checks: + UnusedLocalVariable: + enabled: false + CleanCode/StaticAccess: + enabled: false + + sonar-php: + enabled: true + checks: + php:S1448: + enabled: false diff --git a/examples/example/ArrayValue-skip.php b/examples/example/ArrayValue-skip.php new file mode 100644 index 0000000..9d042c8 --- /dev/null +++ b/examples/example/ArrayValue-skip.php @@ -0,0 +1,8 @@ +skip(2)->toArray()); +echo PHP_EOL; diff --git a/examples/example/ArrayValue-take.php b/examples/example/ArrayValue-take.php new file mode 100644 index 0000000..7669824 --- /dev/null +++ b/examples/example/ArrayValue-take.php @@ -0,0 +1,15 @@ +skip(2)->take(4)->toArray()); +echo PHP_EOL; + +var_export($letters->take(3)->toArray()); +echo PHP_EOL; + +var_export($letters->take(100)->toArray()); +echo PHP_EOL; + diff --git a/examples/example/AssocValue-keys.php b/examples/example/AssocValue-keys.php new file mode 100644 index 0000000..65732e3 --- /dev/null +++ b/examples/example/AssocValue-keys.php @@ -0,0 +1,12 @@ + 'zero', '1' => 'one']); + +$keys = $assoc + ->map(fn(string $val, int $key): string => $val) + ->keys() + ->toArray(); + +var_dump($keys); diff --git a/examples/example/IterableValue-keys.php b/examples/example/IterableValue-keys.php new file mode 100644 index 0000000..26cc7a0 --- /dev/null +++ b/examples/example/IterableValue-keys.php @@ -0,0 +1,29 @@ + 'zero', '1' => 'one']); + +$keys = $assoc + ->map(fn(string $val, int $key): string => $val) + ->keys() + ->toArray(); + +var_dump($keys); + +$pairs = [['0', 'zero'], ['1', 'one'], ['1', 'one one']]; + +$iterator = function () use ($pairs) { + foreach ($pairs as [$key, $item]) { + yield $key => $item; + } +}; + +$assoc = Wrap::iterable($iterator()); + +$keys = $assoc + ->map(fn(string $val, string $key): string => $val) + ->keys() + ->toArray(); + +var_dump($keys); diff --git a/examples/example/IterableValue-take.php b/examples/example/IterableValue-take.php new file mode 100644 index 0000000..53a0406 --- /dev/null +++ b/examples/example/IterableValue-take.php @@ -0,0 +1,18 @@ +skip(2)->take(4)->toArray()); +echo PHP_EOL; + +var_export(Wrap::iterable($range(0, PHP_INT_MAX))->take(3)->toArray()); +echo PHP_EOL; + +var_export(Wrap::iterable($range(0, PHP_INT_MAX))->skip(5000)->take(2)->toArray()); +echo PHP_EOL; diff --git a/spec/AssocArraySpec.php b/spec/AssocArraySpec.php index 38e79f7..db351b0 100644 --- a/spec/AssocArraySpec.php +++ b/spec/AssocArraySpec.php @@ -422,4 +422,11 @@ function it_does_not_allow_mutations_trough_ArrayAccess() $this->shouldThrow(\BadMethodCallException::class)->during('offsetSet', ['a', 'mutated 1']); $this->shouldThrow(\BadMethodCallException::class)->during('offsetUnset', ['a']); } + + function it_handles_numeric_strings_key_as_int_from_array() + { + $this->beConstructedWith(['0' => 'zero', '1' => 'one']); + $this->map(fn(string $val, int $key): string => $val) + ->keys()->toArray()->shouldEqual([0, 1]); + } } diff --git a/spec/InfiniteIterableValueSpec.php b/spec/InfiniteIterableValueSpec.php index 4779a2a..01958ea 100644 --- a/spec/InfiniteIterableValueSpec.php +++ b/spec/InfiniteIterableValueSpec.php @@ -9,6 +9,7 @@ use GW\Value\Wrap; use PhpSpec\Exception\Example\FailureException; use PhpSpec\ObjectBehavior; +use function range; final class InfiniteIterableValueSpec extends ObjectBehavior { @@ -19,6 +20,31 @@ function it_can_be_converted_to_array_value() $this->toArrayValue()->shouldBeLike(Wrap::array($items)); } + function it_can_be_converted_to_simple_assoc_array() + { + $items = ['item 1', 'item 2', 'item 3']; + $this->beConstructedWith($items); + $this->toAssocArray()->shouldBeLike([0 => 'item 1', 1 => 'item 2', 2 => 'item 3']); + } + + function it_can_be_converted_to_keyed_assoc_array() + { + $items = ['foo' => 'item 1', 'bar' => 'item 2']; + $this->beConstructedWith($items); + $this->toAssocArray()->shouldBeLike(['foo' => 'item 1', 'bar' => 'item 2']); + } + + function it_can_be_converted_to_keyed_assoc_array_with_map_filter() + { + $items = ['foo' => 'item 1', 'bar' => 'item 2']; + $this->beConstructedWith($items); + $this + ->map(fn(string $value, string $key): string => "{$value} mod") + ->filter(fn(string $value): bool => true) + ->toAssocArray() + ->shouldBeLike(['foo' => 'item 1 mod', 'bar' => 'item 2 mod']); + } + function it_returns_items() { $items = ['item 1', 'item 2', 'item 3']; @@ -26,6 +52,13 @@ function it_returns_items() $this->toArray()->shouldReturn($items); } + function it_returns_keys() + { + $items = ['item 1', 'item 2', 'item 3']; + $this->beConstructedWith($items); + $this->keys()->toArray()->shouldReturn([0, 1, 2]); + } + function it_maps_string_items_with_closure() { $this->beConstructedWith(['item 1', 'item 2', 'item 3']); @@ -41,8 +74,7 @@ function it_maps_string_items_with_closure() function it_maps_items_with_php_callable() { $this->beConstructedWith(['100', '50.12', '', true, false]); - - $mapped = $this->map('intval'); + $mapped = $this->map(fn($value): int => (int)$value); $mapped->shouldNotBe($this); $mapped->toArray()->shouldBeLike([100, 50, 0, 1, 0]); @@ -184,12 +216,41 @@ function it_slices_given_part() { $this->beConstructedWith(['item 1', 'item 2', 'item 3', 'item 4', 'item 5', 'item 6']); - $this->slice(0, 1)->shouldNotBe($this); + $this->slice(0, 1)->toArray()->shouldNotBe($this->toArray()); $this->slice(0, 1)->toArray()->shouldBeLike(['item 1']); $this->slice(1, 4)->toArray()->shouldBeLike(['item 2', 'item 3', 'item 4', 'item 5']); $this->slice(5, 1)->toArray()->shouldBeLike(['item 6']); } + function it_returns_empty_set_for_slice_with_zero_length() + { + $this->beConstructedWith(['item 1', 'item 2', 'item 3', 'item 4', 'item 5', 'item 6']); + + $this->slice(0, 0)->toArray()->shouldBeLike([]); + $this->slice(1, 0)->toArray()->shouldBeLike([]); + $this->slice(4, 0)->toArray()->shouldBeLike([]); + $this->slice(55, 0)->toArray()->shouldBeLike([]); + } + + function it_throws_when_used_on_negative_offset_or_length_in_slice() + { + $this->beConstructedWith(['item 1', 'item 2', 'item 3', 'item 4', 'item 5', 'item 6']); + + $this->shouldThrow()->during('slice', [-1, 10]); + $this->shouldThrow()->during('slice', [-1, -110]); + $this->shouldThrow()->during('slice', [1, -110]); + } + + function it_skips_and_takes_given_part() + { + $this->beConstructedWith(['item 1', 'item 2', 'item 3', 'item 4', 'item 5', 'item 6']); + + $this->take(1)->toArray()->shouldNotBe($this->toArray()); + $this->take(1)->toArray()->shouldBeLike(['item 1']); + $this->skip(1)->take(4)->toArray()->shouldBeLike(['item 2', 'item 3', 'item 4', 'item 5']); + $this->skip(5)->take(1)->toArray()->shouldBeLike(['item 6']); + } + function it_slice_and_do_not_take_elements_above_end_index() { $takenElements = 0; @@ -518,6 +579,103 @@ function it_finds_null_when_no_item_matches_condition() ->shouldReturn(null); } + function it_handles_repeated_keys_properly_with_toArray() + { + $iterator = function () { + foreach (range(0, 5) as $item) { + yield 0 => $item; + } + }; + + $this->beConstructedWith($iterator()); + $this->toArray()->shouldEqual([0, 1, 2, 3, 4, 5]); + } + + function it_handles_repeated_keys_properly_with_toAssocArray() + { + $iterator = function () { + foreach (range(0, 5) as $item) { + yield 0 => $item; + } + }; + + $this->beConstructedWith($iterator()); + $this->toAssocArray()->shouldEqual([0 => 5]); + } + + function it_handles_repeated_keys_properly_with_keys() + { + $iterator = function () { + foreach (range(0, 5) as $item) { + yield 0 => $item; + } + }; + + $this->beConstructedWith($iterator()); + $this->keys()->toArray()->shouldEqual([0, 0, 0, 0, 0, 0]); + } + + function it_handles_repeated_keys_properly_with_map() + { + $iterator = function () { + foreach (range(0, 5) as $item) { + yield 0 => $item; + } + }; + + $this->beConstructedWith($iterator()); + $this->map(fn(int $value, int $key): int => $key)->toArray()->shouldEqual([0, 0, 0, 0, 0, 0]); + } + + function it_handles_repeated_keys_properly_with_slice() + { + $iterator = function () { + foreach (range(0, 5) as $item) { + yield 0 => $item; + } + }; + + $this->beConstructedWith($iterator()); + $this->slice(1, 2)->toArray()->shouldEqual([1, 2]); + } + + function it_handles_numeric_strings_key_as_int_from_array() + { + $this->beConstructedWith(['0' => 'zero', '1' => 'one']); + $this->map(fn(string $val, int $key): string => $val) + ->keys()->toArray()->shouldEqual([0, 1]); + } + + function it_handles_numeric_strings_key_as_string_from_iterator() + { + $pairs = [['0', 'zero'], ['1', 'one'], ['1', 'one one']]; + + $iterator = function () use ($pairs) { + foreach ($pairs as [$key, $item]) { + yield $key => $item; + } + }; + + $this->beConstructedWith($iterator()); + $this->map(fn(string $val, string $key): string => $val) + ->keys()->toArray()->shouldEqual(['0', '1', '1']); + } + + function it_handles_object_keys() + { + $pairs = [[(object)['foo' => 'bar'], 'zero'], [(object)['foo' => 'baz'], 'one one']]; + + $iterator = function () use ($pairs) { + foreach ($pairs as [$key, $item]) { + yield $key => $item; + } + }; + + $this->beConstructedWith($iterator()); + $this->map(fn(string $val, object $key): string => $val) + ->keys()->toArray()->shouldBeLike([(object)['foo' => 'bar'], (object)['foo' => 'baz']]); + } + private function entityComparator(): \Closure { return function (DummyEntity $entityA, DummyEntity $entityB): int { diff --git a/spec/PlainArraySpec.php b/spec/PlainArraySpec.php index ac79fe6..c0c554c 100644 --- a/spec/PlainArraySpec.php +++ b/spec/PlainArraySpec.php @@ -55,7 +55,7 @@ function it_maps_items_with_php_callable() { $this->beConstructedWith(['100', '50.12', '', true, false]); - $mapped = $this->map('intval'); + $mapped = $this->map(fn($value): int => (int)$value); $mapped->shouldNotBe($this); $mapped->toArray()->shouldBeLike([100, 50, 0, 1, 0]); @@ -341,12 +341,22 @@ function it_slices_given_part() { $this->beConstructedWith(['item 1', 'item 2', 'item 3', 'item 4', 'item 5', 'item 6']); - $this->slice(0, 1)->shouldNotBe($this); + $this->slice(0, 1)->toArray()->shouldNotBe($this->toArray()); $this->slice(0, 1)->toArray()->shouldBeLike(['item 1']); $this->slice(1, 4)->toArray()->shouldBeLike(['item 2', 'item 3', 'item 4', 'item 5']); $this->slice(5, 1)->toArray()->shouldBeLike(['item 6']); } + function it_skips_and_takes_given_part() + { + $this->beConstructedWith(['item 1', 'item 2', 'item 3', 'item 4', 'item 5', 'item 6']); + + $this->take(1)->toArray()->shouldNotBe($this->toArray()); + $this->take(1)->toArray()->shouldBeLike(['item 1']); + $this->skip(1)->take(4)->toArray()->shouldBeLike(['item 2', 'item 3', 'item 4', 'item 5']); + $this->skip(5)->take(1)->toArray()->shouldBeLike(['item 6']); + } + function it_allows_to_remove_slice_from_array_with_splice() { $this->beConstructedWith(['item 1', 'item 2', 'item 3', 'item 4', 'item 5', 'item 6']); diff --git a/spec/PlainStringsArraySpec.php b/spec/PlainStringsArraySpec.php index 405ce4e..42ca689 100644 --- a/spec/PlainStringsArraySpec.php +++ b/spec/PlainStringsArraySpec.php @@ -51,7 +51,7 @@ function it_can_be_sliced() $strings = ['string 1', 'string 2', 'string 3', 'string 4']; $this->beConstructedWithStrings(...$strings); - $this->slice(0, 1)->shouldNotBe($this); + $this->slice(0, 1)->toNativeStrings()->shouldNotBe($this->toNativeStrings()); $this->slice(0, 1)->toNativeStrings()->shouldBeLike(['string 1']); $this->slice(3, 1)->toNativeStrings()->shouldBeLike(['string 4']); $this->slice(1, 2)->toNativeStrings()->shouldBeLike(['string 2', 'string 3']); @@ -59,6 +59,17 @@ function it_can_be_sliced() $this->slice(0, 500)->toNativeStrings()->shouldBeLike($strings); } + function it_skips_and_takes_given_part() + { + $strings = ['item 1', 'item 2', 'item 3', 'item 4', 'item 5', 'item 6']; + $this->beConstructedWithStrings(...$strings); + + $this->take(1)->toNativeStrings()->shouldNotBe($this->toNativeStrings()); + $this->take(1)->toNativeStrings()->shouldBeLike(['item 1']); + $this->skip(1)->take(4)->toNativeStrings()->shouldBeLike(['item 2', 'item 3', 'item 4', 'item 5']); + $this->skip(5)->take(1)->toNativeStrings()->shouldBeLike(['item 6']); + } + function it_can_be_spliced() { $this->beConstructedWithStrings('string 1', 'string 2', 'string 3', 'string 4'); diff --git a/src/ArrayValue.php b/src/ArrayValue.php index 54dfafe..aebc1c1 100644 --- a/src/ArrayValue.php +++ b/src/ArrayValue.php @@ -149,7 +149,17 @@ public function join(ArrayValue $other): ArrayValue; /** * @phpstan-return ArrayValue */ - public function slice(int $offset, int $length): ArrayValue; + public function slice(int $offset, ?int $length = null): ArrayValue; + + /** + * @phpstan-return ArrayValue + */ + public function skip(int $length): ArrayValue; + + /** + * @phpstan-return ArrayValue + */ + public function take(int $length): ArrayValue; /** * @phpstan-param ArrayValue $replacement diff --git a/src/Arrayable/Slice.php b/src/Arrayable/Slice.php index ee4a2ce..ab1c4f9 100644 --- a/src/Arrayable/Slice.php +++ b/src/Arrayable/Slice.php @@ -14,10 +14,10 @@ final class Slice implements Arrayable /** @var Arrayable */ private Arrayable $arrayable; private int $offset; - private int $length; + private ?int $length; /** @param Arrayable $arrayable */ - public function __construct(Arrayable $arrayable, int $offset, int $length) + public function __construct(Arrayable $arrayable, int $offset, ?int $length = null) { $this->arrayable = $arrayable; $this->offset = $offset; diff --git a/src/AssocValue.php b/src/AssocValue.php index 4dc8d8e..4e1e2e1 100644 --- a/src/AssocValue.php +++ b/src/AssocValue.php @@ -41,7 +41,7 @@ public function filterEmpty(): AssocValue; /** * @template TNewValue - * @param callable(TValue $value, TKey $key=):TNewValue $transformer + * @param callable(TValue,TKey $key=):TNewValue $transformer * @phpstan-return AssocValue */ public function map(callable $transformer): AssocValue; diff --git a/src/InfiniteIterableValue.php b/src/InfiniteIterableValue.php index 738e948..612e20d 100644 --- a/src/InfiniteIterableValue.php +++ b/src/InfiniteIterableValue.php @@ -3,6 +3,7 @@ namespace GW\Value; use function count; +use const PHP_INT_MAX; /** * @template TKey @@ -36,7 +37,7 @@ private static function fromStack(IterableValueStack $stack): self } /** - * @param callable(TValue $value):void $callback + * @param callable(TValue):void $callback * @phpstan-return InfiniteIterableValue */ public function each(callable $callback): InfiniteIterableValue @@ -49,7 +50,7 @@ public function each(callable $callback): InfiniteIterableValue } /** - * @param (callable(TValue $lueA,TValue):int) | null $comparator + * @param (callable(TValue,TValue):int) | null $comparator * @phpstan-return InfiniteIterableValue */ public function unique(?callable $comparator = null): InfiniteIterableValue @@ -90,7 +91,7 @@ public function toArray(): array } /** - * @phpstan-param callable(TValue $value):bool $filter + * @phpstan-param callable(TValue):bool $filter * @phpstan-return InfiniteIterableValue */ public function filter(callable $filter): InfiniteIterableValue @@ -101,9 +102,9 @@ public function filter(callable $filter): InfiniteIterableValue * @phpstan-return iterable */ static function (iterable $iterable) use ($filter): iterable { - foreach ($iterable as $value) { + foreach ($iterable as $key => $value) { if ($filter($value)) { - yield $value; + yield $key => $value; } } } @@ -120,7 +121,7 @@ public function filterEmpty(): InfiniteIterableValue /** * @template TNewValue - * @param callable(TValue $value): TNewValue $transformer + * @param callable(TValue,TKey $key=):TNewValue $transformer * @phpstan-return InfiniteIterableValue */ public function map(callable $transformer): InfiniteIterableValue @@ -131,8 +132,8 @@ public function map(callable $transformer): InfiniteIterableValue * @phpstan-return iterable */ static function (iterable $iterable) use ($transformer): iterable { - foreach ($iterable as $value) { - yield $transformer($value); + foreach ($iterable as $key => $value) { + yield $key => $transformer($value, $key); } } )); @@ -140,7 +141,7 @@ static function (iterable $iterable) use ($transformer): iterable { /** * @template TNewValue - * @param callable(TValue $value): iterable $transformer + * @param callable(TValue):iterable $transformer * @phpstan-return InfiniteIterableValue */ public function flatMap(callable $transformer): InfiniteIterableValue @@ -166,6 +167,28 @@ public function toArrayValue(): ArrayValue return Wrap::array($this->toArray()); } + /** + * @phpstan-return array + */ + public function toAssocArray(): array + { + $return = []; + + foreach ($this->stack->iterate() as $key => $value) { + $return[$key] = $value; + } + + return $return; + } + + /** + * @phpstan-return AssocValue + */ + public function toAssocValue(): AssocValue + { + return Wrap::assocArray($this->toAssocArray()); + } + /** * @phpstan-param TValue $value * @phpstan-return InfiniteIterableValue @@ -223,22 +246,33 @@ static function (iterable $iterable) use ($other): iterable { /** * @phpstan-return InfiniteIterableValue */ - public function slice(int $offset, int $length): InfiniteIterableValue + public function slice(int $offset, ?int $length = null): InfiniteIterableValue { + if ($offset < 0) { + throw new \InvalidArgumentException('Start offset must be non-negative'); + } + if ($length < 0) { + throw new \InvalidArgumentException('Length must be non-negative'); + } + if ($length === 0) { + return new self([]); + } + $offsetSet = $offset > 0; + return self::fromStack($this->stack->push( /** * @phpstan-param iterable $iterable * @phpstan-return iterable */ - static function (iterable $iterable) use ($offset, $length): iterable { - foreach ($iterable as $value) { - if ($offset-- > 0) { + static function (iterable $iterable) use ($offsetSet, $offset, $length): iterable { + foreach ($iterable as $key => $value) { + if ($offsetSet && $offset-- > 0) { continue; } - yield $value; + yield $key => $value; - if (--$length <= 0) { + if ($length !== null && --$length <= 0) { break; } } @@ -246,9 +280,25 @@ static function (iterable $iterable) use ($offset, $length): iterable { )); } + /** + * @phpstan-return InfiniteIterableValue + */ + public function skip(int $length): InfiniteIterableValue + { + return $this->slice($length); + } + + /** + * @phpstan-return InfiniteIterableValue + */ + public function take(int $length): InfiniteIterableValue + { + return $this->slice(0, $length); + } + /** * @phpstan-param ArrayValue $other - * @param (callable(TValue,TValue):int) | null $comparator + * @phpstan-param (callable(TValue,TValue):int) | null $comparator * @phpstan-return InfiniteIterableValue */ public function diff(ArrayValue $other, ?callable $comparator = null): InfiniteIterableValue @@ -278,7 +328,7 @@ static function (iterable $iterable) use ($other, $comparator): iterable { /** * @phpstan-param ArrayValue $other - * @param callable(TValue $valueA, TValue $valueB):int | null $comparator + * @phpstan-param (callable(TValue,TValue):int) | null $comparator * @phpstan-return InfiniteIterableValue */ public function intersect(ArrayValue $other, ?callable $comparator = null): InfiniteIterableValue @@ -459,6 +509,24 @@ public function last() return $value; } + /** + * @phpstan-return InfiniteIterableValue + */ + public function keys(): InfiniteIterableValue + { + return self::fromStack($this->stack->push( + /** + * @phpstan-param iterable $iterable + * @phpstan-return iterable + */ + static function (iterable $iterable): iterable { + foreach ($iterable as $key => $value) { + yield $key; + } + } + )); + } + /** * @phpstan-return iterable */ diff --git a/src/IterableValue.php b/src/IterableValue.php index 0e63793..a3f4704 100644 --- a/src/IterableValue.php +++ b/src/IterableValue.php @@ -3,6 +3,7 @@ namespace GW\Value; use IteratorAggregate; +use const PHP_INT_MAX; /** * @template TKey @@ -14,13 +15,13 @@ interface IterableValue extends IteratorAggregate // IterableValue own /** - * @phpstan-param callable(TValue $value):void $callback + * @phpstan-param callable(TValue):void $callback * @phpstan-return IterableValue */ public function each(callable $callback): IterableValue; /** - * @phpstan-param callable(TValue $value):bool $filter + * @phpstan-param callable(TValue):bool $filter * @phpstan-return IterableValue */ public function filter(callable $filter): IterableValue; @@ -32,14 +33,14 @@ public function filterEmpty(): IterableValue; /** * @template TNewValue - * @phpstan-param callable(TValue $value): TNewValue $transformer + * @param callable(TValue,TKey $key=):TNewValue $transformer * @phpstan-return IterableValue */ public function map(callable $transformer): IterableValue; /** * @phpstan-template TNewValue - * @phpstan-param callable(TValue $value): iterable $transformer + * @phpstan-param callable(TValue):iterable $transformer * @phpstan-return IterableValue */ public function flatMap(callable $transformer): IterableValue; @@ -53,17 +54,27 @@ public function join(iterable $other): IterableValue; /** * @phpstan-return IterableValue */ - public function slice(int $offset, int $length): IterableValue; + public function slice(int $offset, ?int $length = null): IterableValue; /** - * @phpstan-param callable(TValue $valueA, TValue $valueB):int | null $comparator + * @phpstan-return IterableValue + */ + public function skip(int $length): IterableValue; + + /** + * @phpstan-return IterableValue + */ + public function take(int $length): IterableValue; + + /** + * @phpstan-param (callable(TValue,TValue):int) | null $comparator * @phpstan-return IterableValue */ public function unique(?callable $comparator = null): IterableValue; /** * @template TNewValue - * @phpstan-param callable(TNewValue $reduced, TValue $value): TNewValue $transformer + * @phpstan-param callable(TNewValue,TValue):TNewValue $transformer * @phpstan-param TNewValue $start * @phpstan-return TNewValue */ @@ -88,14 +99,14 @@ public function push($value): IterableValue; /** * @phpstan-param ArrayValue $other - * @phpstan-param callable(TValue $valueA, TValue $valueB):int | null $comparator + * @phpstan-param (callable(TValue,TValue):int) | null $comparator * @phpstan-return IterableValue */ public function diff(ArrayValue $other, ?callable $comparator = null): IterableValue; /** * @phpstan-param ArrayValue $other - * @phpstan-param callable(TValue $valueA, TValue $valueB):int | null $comparator + * @phpstan-param (callable(TValue,TValue):int) | null $comparator * @phpstan-return IterableValue */ public function intersect(ArrayValue $other, ?callable $comparator = null): IterableValue; @@ -115,6 +126,16 @@ public function last(); */ public function toArrayValue(): ArrayValue; + /** + * @phpstan-return array + */ + public function toAssocArray(): array; + + /** + * @phpstan-return AssocValue + */ + public function toAssocValue(): AssocValue; + /** * @phpstan-return TValue[] */ @@ -131,24 +152,29 @@ public function chunk(int $size): IterableValue; public function flatten(): IterableValue; /** - * @param callable(TValue $value):bool $filter + * @param callable(TValue):bool $filter */ public function any(callable $filter): bool; /** - * @param callable(TValue $value):bool $filter + * @param callable(TValue):bool $filter */ public function every(callable $filter): bool; /** - * @param callable(TValue $value):bool $filter + * @param callable(TValue):bool $filter * @phpstan-return ?TValue */ public function find(callable $filter); /** - * @param callable(TValue $value):bool $filter + * @param callable(TValue):bool $filter * @phpstan-return ?TValue */ public function findLast(callable $filter); + + /** + * @phpstan-return IterableValue + */ + public function keys(): IterableValue; } diff --git a/src/PlainArray.php b/src/PlainArray.php index c41a087..5037c46 100644 --- a/src/PlainArray.php +++ b/src/PlainArray.php @@ -155,11 +155,27 @@ public function join(ArrayValue $other): PlainArray /** * @phpstan-return PlainArray */ - public function slice(int $offset, int $length): PlainArray + public function slice(int $offset, ?int $length = null): PlainArray { return new self(new Slice($this->items, $offset, $length)); } + /** + * @phpstan-return PlainArray + */ + public function skip(int $length): PlainArray + { + return $this->slice($length); + } + + /** + * @phpstan-return PlainArray + */ + public function take(int $length): PlainArray + { + return $this->slice(0, $length); + } + /** * @phpstan-param ArrayValue $replacement * @phpstan-return PlainArray diff --git a/src/PlainStringsArray.php b/src/PlainStringsArray.php index b74e437..b050955 100644 --- a/src/PlainStringsArray.php +++ b/src/PlainStringsArray.php @@ -32,11 +32,21 @@ public function join(StringsArray $other): PlainStringsArray return new self($this->strings->join($other->toArrayValue())); } - public function slice(int $offset, int $length): PlainStringsArray + public function slice(int $offset, ?int $length = null): PlainStringsArray { return new self($this->strings->slice($offset, $length)); } + public function skip(int $length): PlainStringsArray + { + return $this->slice($length, $this->count() - $length); + } + + public function take(int $length): PlainStringsArray + { + return $this->slice(0, $length); + } + public function splice(int $offset, int $length, ?StringsArray $replacement = null): PlainStringsArray { return new self( diff --git a/src/StringsArray.php b/src/StringsArray.php index aebab13..3357cb6 100644 --- a/src/StringsArray.php +++ b/src/StringsArray.php @@ -108,21 +108,23 @@ public function offsetGet($offset): StringValue; /** * @param int $offset * @param StringValue|string $value - * @return void * @throws BadMethodCallException For immutable types. */ public function offsetSet($offset, $value): void; /** * @param int $offset - * @return void * @throws BadMethodCallException For immutable types. */ public function offsetUnset($offset): void; public function join(StringsArray $other): StringsArray; - public function slice(int $offset, int $length): StringsArray; + public function slice(int $offset, ?int $length = null): StringsArray; + + public function skip(int $length): StringsArray; + + public function take(int $length): StringsArray; public function splice(int $offset, int $length, ?StringsArray $replacement = null): StringsArray; @@ -162,7 +164,7 @@ public function first(): ?StringValue; public function last(): ?StringValue; /** - * @param callable(StringValue $value): bool $filter + * @param callable(StringValue):bool $filter */ public function find(callable $filter): ?StringValue;