From fbc34393eb1dee69a126ab8a4972f308d95f6a7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bronis=C5=82aw=20Bia=C5=82ek?= Date: Tue, 21 Jun 2022 20:19:14 +0200 Subject: [PATCH 1/6] Add AssocValue flip and swap functions --- spec/AssocArraySpec.php | 46 +++++++++++++++++++++++++++++++++++++++++ src/AssocArray.php | 19 ++++++++++++++++- src/Associable/Flip.php | 31 +++++++++++++++++++++++++++ src/Associable/Swap.php | 44 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 src/Associable/Flip.php create mode 100644 src/Associable/Swap.php diff --git a/spec/AssocArraySpec.php b/spec/AssocArraySpec.php index b6fc008..e2ece2e 100644 --- a/spec/AssocArraySpec.php +++ b/spec/AssocArraySpec.php @@ -517,4 +517,50 @@ function it_handles_numeric_strings_key_as_int_from_array() $this->map(fn(string $val, int $key): string => $val) ->keys()->toArray()->shouldEqual([0, 1]); } + + function it_can_be_flipped() + { + $this->beConstructedWith(['foo' => 'zero', 'bar' => 'one']); + $this->flip() + ->keys()->toArray()->shouldEqual(['zero', 'one']); + $this->flip() + ->values()->toArray()->shouldEqual(['foo', 'bar']); + } + + function it_can_be_flipped_numeric_keys() + { + $this->beConstructedWith(['0' => 'zero', '1' => 'one']); + $this->flip() + ->keys()->toArray()->shouldEqual(['zero', 'one']); + $this->flip() + ->values()->toArray()->shouldEqual([0, 1]); + } + + function it_is_possible_to_swap_keys() + { + $this->beConstructedWith(['foo' => 'zero', 'bar' => 'one']); + $this->swap('foo', 'bar') + ->toAssocArray()->shouldEqual(['foo' => 'one', 'bar' => 'zero']); + } + + function it_is_possible_to_swap_keys_undefined_a() + { + $this->beConstructedWith(['foo' => 'zero', 'bar' => 'one']); + $this->swap('baz', 'bar') + ->toAssocArray()->shouldEqual(['foo' => 'zero', 'bar' => null, 'baz' => 'one']); + } + + function it_is_possible_to_swap_keys_undefined_b() + { + $this->beConstructedWith(['foo' => 'zero', 'bar' => 'one']); + $this->swap('bar', 'baz') + ->toAssocArray()->shouldEqual(['foo' => 'zero', 'bar' => null, 'baz' => 'one']); + } + + function it_is_possible_to_swap_keys_numeric_arrays() + { + $this->beConstructedWith(['0' => 'zero', '1' => 'one']); + $this->swap(0, 1) + ->toAssocArray()->shouldEqual([0 => 'one', 1 => 'zero']); + } } diff --git a/src/AssocArray.php b/src/AssocArray.php index d87b424..d3d83b3 100644 --- a/src/AssocArray.php +++ b/src/AssocArray.php @@ -2,10 +2,10 @@ namespace GW\Value; -use ArrayIterator; use BadMethodCallException; use GW\Value\Associable\Cache; use GW\Value\Associable\Filter; +use GW\Value\Associable\Flip; use GW\Value\Associable\Join; use GW\Value\Associable\JustAssoc; use GW\Value\Associable\Keys; @@ -18,6 +18,7 @@ use GW\Value\Associable\Shuffle; use GW\Value\Associable\Sort; use GW\Value\Associable\SortKeys; +use GW\Value\Associable\Swap; use GW\Value\Associable\UniqueByComparator; use GW\Value\Associable\UniqueByString; use GW\Value\Associable\Values; @@ -216,6 +217,22 @@ public function withoutElement($value): AssocArray return $this->filter(Filters::notEqual($value)); } + /** + * @phpstan-return AssocArray + */ + public function flip(): AssocArray + { + return new self(new Flip($this->items)); + } + + /** + * @phpstan-return AssocArray + */ + public function swap($keyA, $keyB): AssocArray + { + return new self(new Swap($this->items, $keyA, $keyB)); + } + /** * @template TNewValue * @param callable(TNewValue,TValue,TKey):TNewValue $transformer diff --git a/src/Associable/Flip.php b/src/Associable/Flip.php new file mode 100644 index 0000000..a5df5c4 --- /dev/null +++ b/src/Associable/Flip.php @@ -0,0 +1,31 @@ + + */ +final class Flip implements Associable +{ + /** @var Associable */ + private Associable $associable; + + /** + * @param Associable $associable + */ + public function __construct(Associable $associable) + { + $this->associable = $associable; + } + + /** @return array */ + public function toAssocArray(): array + { + return array_flip($this->associable->toAssocArray()); + } +} diff --git a/src/Associable/Swap.php b/src/Associable/Swap.php new file mode 100644 index 0000000..bbc5c55 --- /dev/null +++ b/src/Associable/Swap.php @@ -0,0 +1,44 @@ + + */ +final class Swap implements Associable +{ + /** @var Associable */ + private Associable $associable; + /** @var TKey */ + private int|string $keyA; + /** @var TKey */ + private int|string $keyB; + + /** + * @param Associable $associable + * @param TKey $keyA + * @param TKey $keyB + */ + public function __construct(Associable $associable, int|string $keyA, int|string $keyB) + { + $this->associable = $associable; + $this->keyA = $keyA; + $this->keyB = $keyB; + } + + /** @return array */ + public function toAssocArray(): array + { + $items = $this->associable->toAssocArray(); + $valueA = $items[$this->keyA] ?? null; + $valueB = $items[$this->keyB] ?? null; + $items[$this->keyA] = $valueB; + $items[$this->keyB] = $valueA; + + return $items; + } +} From 55522111199d4882d2ce7acc2b67525518a14b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bronis=C5=82aw=20Bia=C5=82ek?= Date: Tue, 21 Jun 2022 20:20:36 +0200 Subject: [PATCH 2/6] Add AssocValue flip and swap functions --- src/AssocArray.php | 2 +- src/AssocValue.php | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/AssocArray.php b/src/AssocArray.php index d3d83b3..49818b0 100644 --- a/src/AssocArray.php +++ b/src/AssocArray.php @@ -228,7 +228,7 @@ public function flip(): AssocArray /** * @phpstan-return AssocArray */ - public function swap($keyA, $keyB): AssocArray + public function swap(int|string $keyA, int|string $keyB): AssocArray { return new self(new Swap($this->items, $keyA, $keyB)); } diff --git a/src/AssocValue.php b/src/AssocValue.php index 682b24e..a74ea36 100644 --- a/src/AssocValue.php +++ b/src/AssocValue.php @@ -3,6 +3,7 @@ namespace GW\Value; use BadMethodCallException; +use GW\Value\Associable\Flip; use IteratorAggregate; use ArrayAccess; @@ -143,6 +144,16 @@ public function only(...$keys): AssocValue; */ public function withoutElement($value): AssocValue; + /** + * @phpstan-return AssocArray + */ + public function flip(): AssocArray; + + /** + * @phpstan-return AssocArray + */ + public function swap(int|string $keyA, int|string $keyB): AssocArray; + /** * @deprecated use join() or replace() instead * @phpstan-param AssocValue $other From d56dc0836c55fa2cbc1d1822331242510a7a91d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bronis=C5=82aw=20Bia=C5=82ek?= Date: Tue, 21 Jun 2022 20:44:27 +0200 Subject: [PATCH 3/6] Add AssocValue flip and swap functions --- spec/AssocArraySpec.php | 15 +++++++----- spec/InfiniteIterableValueSpec.php | 24 +++++++++++++++++++ src/AssocArray.php | 18 +++++++++++---- src/AssocValue.php | 8 ++++--- src/Associable/Flip.php | 6 ++--- src/Associable/Swap.php | 11 +++++---- src/InfiniteIterableValue.php | 37 ++++++++++++++++++++++++++++++ src/IterableValue.php | 10 ++++++++ 8 files changed, 108 insertions(+), 21 deletions(-) diff --git a/spec/AssocArraySpec.php b/spec/AssocArraySpec.php index e2ece2e..06e262b 100644 --- a/spec/AssocArraySpec.php +++ b/spec/AssocArraySpec.php @@ -5,6 +5,7 @@ use GW\Value\Filters; use GW\Value\Wrap; use GW\Value\AssocArray; +use InvalidArgumentException; use PhpSpec\ObjectBehavior; use PHPUnit\Framework\Assert; @@ -543,18 +544,20 @@ function it_is_possible_to_swap_keys() ->toAssocArray()->shouldEqual(['foo' => 'one', 'bar' => 'zero']); } - function it_is_possible_to_swap_keys_undefined_a() + function it_is_impossible_to_swap_keys_undefined_a() { $this->beConstructedWith(['foo' => 'zero', 'bar' => 'one']); - $this->swap('baz', 'bar') - ->toAssocArray()->shouldEqual(['foo' => 'zero', 'bar' => null, 'baz' => 'one']); + $swapped = $this->swap('baz', 'bar'); + $swapped->shouldThrow(InvalidArgumentException::class) + ->during('toAssocArray'); } - function it_is_possible_to_swap_keys_undefined_b() + function it_is_impossible_to_swap_keys_undefined_b() { $this->beConstructedWith(['foo' => 'zero', 'bar' => 'one']); - $this->swap('bar', 'baz') - ->toAssocArray()->shouldEqual(['foo' => 'zero', 'bar' => null, 'baz' => 'one']); + $swapped = $this->swap('bar', 'baz'); + $swapped->shouldThrow(InvalidArgumentException::class) + ->during('toAssocArray'); } function it_is_possible_to_swap_keys_numeric_arrays() diff --git a/spec/InfiniteIterableValueSpec.php b/spec/InfiniteIterableValueSpec.php index 01958ea..afd3fa2 100644 --- a/spec/InfiniteIterableValueSpec.php +++ b/spec/InfiniteIterableValueSpec.php @@ -676,6 +676,30 @@ function it_handles_object_keys() ->keys()->toArray()->shouldBeLike([(object)['foo' => 'bar'], (object)['foo' => 'baz']]); } + function it_can_be_flipped() + { + $this->beConstructedWith(['foo' => 'zero', 'bar' => 'one']); + $this->flip() + ->keys()->toArray()->shouldEqual(['zero', 'one']); + $this->flip() + ->values()->toArray()->shouldEqual(['foo', 'bar']); + } + + function it_can_be_flipped_numeric_keys() + { + $pairs = [['0', 'zero'], ['1', 'one']]; + + $iterator = function () use ($pairs) { + foreach ($pairs as [$key, $item]) { + yield $key => $item; + } + }; + + $this->beConstructedWith($iterator()); + $this->flip() + ->values()->toArray()->shouldEqual(['0', '1']); + } + private function entityComparator(): \Closure { return function (DummyEntity $entityA, DummyEntity $entityB): int { diff --git a/src/AssocArray.php b/src/AssocArray.php index 49818b0..150aa50 100644 --- a/src/AssocArray.php +++ b/src/AssocArray.php @@ -218,17 +218,27 @@ public function withoutElement($value): AssocArray } /** - * @phpstan-return AssocArray + * @phpstan-return IterableValue + */ + public function toIterableValue(): IterableValue + { + return new InfiniteIterableValue($this->toAssocArray()); + } + + /** + * @phpstan-return IterableValue */ - public function flip(): AssocArray + public function flip(): IterableValue { - return new self(new Flip($this->items)); + return $this->toIterableValue()->flip(); } /** + * @param TKey $keyA + * @param TKey $keyB * @phpstan-return AssocArray */ - public function swap(int|string $keyA, int|string $keyB): AssocArray + public function swap($keyA, $keyB): AssocArray { return new self(new Swap($this->items, $keyA, $keyB)); } diff --git a/src/AssocValue.php b/src/AssocValue.php index a74ea36..621074f 100644 --- a/src/AssocValue.php +++ b/src/AssocValue.php @@ -145,14 +145,16 @@ public function only(...$keys): AssocValue; public function withoutElement($value): AssocValue; /** - * @phpstan-return AssocArray + * @phpstan-return IterableValue */ - public function flip(): AssocArray; + public function flip(): IterableValue; /** + * @param TKey $keyA + * @param TKey $keyB * @phpstan-return AssocArray */ - public function swap(int|string $keyA, int|string $keyB): AssocArray; + public function swap($keyA, $keyB): AssocArray; /** * @deprecated use join() or replace() instead diff --git a/src/Associable/Flip.php b/src/Associable/Flip.php index a5df5c4..0c4f379 100644 --- a/src/Associable/Flip.php +++ b/src/Associable/Flip.php @@ -7,16 +7,16 @@ /** * @template TKey of int|string - * @template TValue + * @template TValue of int|string * @implements Associable */ final class Flip implements Associable { - /** @var Associable */ + /** @var Associable */ private Associable $associable; /** - * @param Associable $associable + * @param Associable $associable */ public function __construct(Associable $associable) { diff --git a/src/Associable/Swap.php b/src/Associable/Swap.php index bbc5c55..ee28ab5 100644 --- a/src/Associable/Swap.php +++ b/src/Associable/Swap.php @@ -3,6 +3,7 @@ namespace GW\Value\Associable; use GW\Value\Associable; +use InvalidArgumentException; /** * @template TKey of int|string @@ -14,16 +15,16 @@ final class Swap implements Associable /** @var Associable */ private Associable $associable; /** @var TKey */ - private int|string $keyA; + private $keyA; /** @var TKey */ - private int|string $keyB; + private $keyB; /** * @param Associable $associable * @param TKey $keyA * @param TKey $keyB */ - public function __construct(Associable $associable, int|string $keyA, int|string $keyB) + public function __construct(Associable $associable, $keyA, $keyB) { $this->associable = $associable; $this->keyA = $keyA; @@ -34,8 +35,8 @@ public function __construct(Associable $associable, int|string $keyA, int|string public function toAssocArray(): array { $items = $this->associable->toAssocArray(); - $valueA = $items[$this->keyA] ?? null; - $valueB = $items[$this->keyB] ?? null; + $valueA = $items[$this->keyA] ?? throw new InvalidArgumentException("Undefined key {$this->keyA}"); + $valueB = $items[$this->keyB] ?? throw new InvalidArgumentException("Undefined key {$this->keyB}"); $items[$this->keyA] = $valueB; $items[$this->keyB] = $valueA; diff --git a/src/InfiniteIterableValue.php b/src/InfiniteIterableValue.php index 51296f2..9135e97 100644 --- a/src/InfiniteIterableValue.php +++ b/src/InfiniteIterableValue.php @@ -4,6 +4,7 @@ use Traversable; use function count; +use function var_dump; use const PHP_INT_MAX; /** @@ -512,6 +513,24 @@ public function last() return $value; } + /** + * @phpstan-return InfiniteIterableValue + */ + public function values(): InfiniteIterableValue + { + return self::fromStack($this->stack->push( + /** + * @phpstan-param iterable $iterable + * @phpstan-return iterable + */ + static function (iterable $iterable): iterable { + foreach ($iterable as $value) { + yield $value; + } + } + )); + } + /** * @phpstan-return InfiniteIterableValue */ @@ -530,6 +549,24 @@ static function (iterable $iterable): iterable { )); } + /** + * @phpstan-return IterableValue + */ + public function flip(): IterableValue + { + return self::fromStack($this->stack->push( + /** + * @phpstan-param iterable $iterable + * @phpstan-return iterable + */ + static function (iterable $iterable): iterable { + foreach ($iterable as $key => $item) { + yield $item => $key; + } + } + )); + } + /** * @phpstan-return Traversable */ diff --git a/src/IterableValue.php b/src/IterableValue.php index a3f4704..1a76a70 100644 --- a/src/IterableValue.php +++ b/src/IterableValue.php @@ -177,4 +177,14 @@ public function findLast(callable $filter); * @phpstan-return IterableValue */ public function keys(): IterableValue; + + /** + * @phpstan-return IterableValue + */ + public function values(): IterableValue; + + /** + * @phpstan-return IterableValue + */ + public function flip(): IterableValue; } From c69909a08eacf7576a6e9b3b4f93c02ca927539a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bronis=C5=82aw=20Bia=C5=82ek?= Date: Tue, 21 Jun 2022 22:04:45 +0200 Subject: [PATCH 4/6] Change flip of assoc --- src/AssocArray.php | 6 +++--- src/AssocValue.php | 4 ++-- src/Associable/Flip.php | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/AssocArray.php b/src/AssocArray.php index 150aa50..11fdcac 100644 --- a/src/AssocArray.php +++ b/src/AssocArray.php @@ -226,11 +226,11 @@ public function toIterableValue(): IterableValue } /** - * @phpstan-return IterableValue + * @phpstan-return AssocValue */ - public function flip(): IterableValue + public function flip(): AssocValue { - return $this->toIterableValue()->flip(); + return new self(new Flip($this->items)); } /** diff --git a/src/AssocValue.php b/src/AssocValue.php index 621074f..02d2fa0 100644 --- a/src/AssocValue.php +++ b/src/AssocValue.php @@ -145,9 +145,9 @@ public function only(...$keys): AssocValue; public function withoutElement($value): AssocValue; /** - * @phpstan-return IterableValue + * @phpstan-return AssocValue */ - public function flip(): IterableValue; + public function flip(): AssocValue; /** * @param TKey $keyA diff --git a/src/Associable/Flip.php b/src/Associable/Flip.php index 0c4f379..2a71df3 100644 --- a/src/Associable/Flip.php +++ b/src/Associable/Flip.php @@ -6,9 +6,9 @@ use function array_flip; /** - * @template TKey of int|string + * @template TKey * @template TValue of int|string - * @implements Associable + * @implements Associable */ final class Flip implements Associable { @@ -23,7 +23,7 @@ public function __construct(Associable $associable) $this->associable = $associable; } - /** @return array */ + /** @return array */ public function toAssocArray(): array { return array_flip($this->associable->toAssocArray()); From b58a76910b3fd3d0de58f14abb50f60116fdd906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bronis=C5=82aw=20Bia=C5=82ek?= Date: Sat, 30 Sep 2023 18:51:45 +0200 Subject: [PATCH 5/6] dedump --- src/InfiniteIterableValue.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/InfiniteIterableValue.php b/src/InfiniteIterableValue.php index 9135e97..6ba0753 100644 --- a/src/InfiniteIterableValue.php +++ b/src/InfiniteIterableValue.php @@ -4,7 +4,6 @@ use Traversable; use function count; -use function var_dump; use const PHP_INT_MAX; /** From 2f55b509cce2743f2996dfb65a2b4175063c4ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bronis=C5=82aw=20Bia=C5=82ek?= Date: Sat, 30 Sep 2023 21:15:01 +0200 Subject: [PATCH 6/6] fix --- src/PlainString.php | 2 +- src/PlainStringsArray.php | 2 +- src/StringValue.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PlainString.php b/src/PlainString.php index d07d534..e793c9d 100644 --- a/src/PlainString.php +++ b/src/PlainString.php @@ -247,7 +247,7 @@ public function endsWith($pattern): bool /** * @param string|StringValue $pattern - * @return ArrayValue> + * @return ArrayValue> */ public function matchAllPatterns($pattern): ArrayValue { diff --git a/src/PlainStringsArray.php b/src/PlainStringsArray.php index 551f6dc..0a4e430 100644 --- a/src/PlainStringsArray.php +++ b/src/PlainStringsArray.php @@ -499,7 +499,7 @@ public function positionLast($needle): ?int /** * @param string|StringValue $pattern - * @return ArrayValue> + * @return ArrayValue> */ public function matchAllPatterns($pattern): ArrayValue { diff --git a/src/StringValue.php b/src/StringValue.php index b8bee1c..2950817 100644 --- a/src/StringValue.php +++ b/src/StringValue.php @@ -148,7 +148,7 @@ public function positionLast($needle): ?int; /** * @param string|StringValue $pattern - * @return ArrayValue> + * @return ArrayValue> */ public function matchAllPatterns($pattern): ArrayValue;