diff --git a/src/Collections/Collection.php b/src/Collections/Collection.php index cf58f32..208b964 100644 --- a/src/Collections/Collection.php +++ b/src/Collections/Collection.php @@ -88,6 +88,27 @@ class Collection extends ImmutableCollection implements ICollection { return $this; } + /** + * @inheritDoc + */ + public function removeFirst () { + $firstKey = $this->firstKey(); + $firstValue = $this->get($firstKey); + + $this->remove($firstKey); + return $firstValue; + } + /** + * @inheritDoc + */ + public function removeLast () { + $lastKey = $this->lastKey(); + $lastValue = $this->get($lastKey); + + $this->remove($lastKey); + return $lastValue; + } + /** * @inheritDoc */ @@ -105,6 +126,8 @@ class Collection extends ImmutableCollection implements ICollection { * @inheritDoc */ public function sliceSelf (int $offset, ?int $length = null): static { + $this->_checkOffset($offset); + $this->elements = array_slice($this->elements, $offset, $length, true); return $this; } diff --git a/src/Collections/ICollection.php b/src/Collections/ICollection.php index d6c6b4c..caf4897 100644 --- a/src/Collections/ICollection.php +++ b/src/Collections/ICollection.php @@ -3,6 +3,7 @@ namespace jrosset\Collections; use Closure; +use OutOfBoundsException; use Throwable; /** @@ -90,6 +91,23 @@ interface ICollection extends IImmutableCollection { */ public function removeValue ($value, bool $strict = false): static; + /** + * Get and remove the first element + * + * @return TValue The value + * + * @throws OutOfBoundsException If the collection is empty + */ + public function removeFirst (); + /** + * Get and remove the last element + * + * @return TValue The value + * + * @throws OutOfBoundsException If the collection is empty + */ + public function removeLast (); + /** * Keep only a slice of the collection * @@ -99,6 +117,8 @@ interface ICollection extends IImmutableCollection { * @param int|null $length The maximum length. Null if until end of the collection * * @return $this + * + * @throws OutOfBoundsException If the offset is not valid */ public function sliceSelf (int $offset, ?int $length = null): static; diff --git a/src/Collections/IImmutableCollection.php b/src/Collections/IImmutableCollection.php index 7867f67..f495caa 100644 --- a/src/Collections/IImmutableCollection.php +++ b/src/Collections/IImmutableCollection.php @@ -7,6 +7,7 @@ use Closure; use Countable; use IteratorAggregate; use JsonSerializable; +use OutOfBoundsException; use Throwable; /** @@ -52,7 +53,18 @@ interface IImmutableCollection extends IteratorAggregate, JsonSerializable, Coun * * @return TValue|null The value */ - public function get ($key): mixed; + public function get ($key); + /** + * Get the value at an offset + * + * @param int $offset The offset + * + * @return TValue The value + * + * @throws OutOfBoundsException If the offset is not valid + */ + public function getFromOffset (int $offset); + /** * Get the first key of a value or null if not found * @@ -62,6 +74,50 @@ interface IImmutableCollection extends IteratorAggregate, JsonSerializable, Coun * @return TKey|null */ public function key ($value, bool $strict = false); + /** + * Get the key at an offset + * + * @param int $offset The offset + * + * @return TKey The key + * + * @throws OutOfBoundsException If the offset is not valid + */ + public function keyFromOffset (int $offset); + + /** + * Get the value of the first element + * + * @return TValue The value + * + * @throws OutOfBoundsException If the collection is empty + */ + public function first (); + /** + * Get the key of the first element + * + * @return TKey The key + * + * @throws OutOfBoundsException If the collection is empty + */ + public function firstKey (); + + /** + * Get the value of the last element + * + * @return TValue The value + * + * @throws OutOfBoundsException If the collection is empty + */ + public function last (); + /** + * Get the key of the last element + * + * @return TKey The key + * + * @throws OutOfBoundsException If the collection is empty + */ + public function lastKey (); /** * Extract a slice of the collection @@ -72,6 +128,8 @@ interface IImmutableCollection extends IteratorAggregate, JsonSerializable, Coun * @param int|null $length The maximum length. Null if until end of the collection * * @return static The result collection + * + * @throws OutOfBoundsException If the offset is not valid */ public function slice (int $offset, ?int $length = null): static; diff --git a/src/Collections/ImmutableCollection.php b/src/Collections/ImmutableCollection.php index fc8bc1c..fb62bb7 100644 --- a/src/Collections/ImmutableCollection.php +++ b/src/Collections/ImmutableCollection.php @@ -5,6 +5,7 @@ namespace jrosset\Collections; use ArrayIterator; use Closure; use InvalidArgumentException; +use OutOfBoundsException; use Traversable; /** @@ -124,6 +125,20 @@ class ImmutableCollection implements IImmutableCollection { protected function _normalizeKey ($key) { return $key; } + /** + * Vérifie un offset (≥ 0, < {@see static::count()}) + * + * @param int $offset L'offset à vérifier + * + * @return void + * + * @throws OutOfBoundsException Si l'offset est invalide + */ + protected function _checkOffset (int $offset): void { + if ($offset < 0 || $offset >= $this->count()) { + throw new OutOfBoundsException('The offset must be between 0 and ' . ($this->count() - 1)); + } + } /** * @inheritDoc @@ -217,6 +232,14 @@ class ImmutableCollection implements IImmutableCollection { public function get ($key): mixed { return $this->elements[$this->_normalizeKey($key)] ?? null; } + /** + * @inheritDoc + */ + public function getFromOffset (int $offset) { + $this->_checkOffset($offset); + return array_values($this->elements)[$offset]; + } + /** * @inheritDoc */ @@ -228,11 +251,46 @@ class ImmutableCollection implements IImmutableCollection { } return null; } + /** + * @inheritDoc + */ + public function keyFromOffset (int $offset) { + $this->_checkOffset($offset); + return array_keys($this->elements)[$offset]; + } + + /** + * @inheritDoc + */ + public function first () { + return $this->getFromOffset(0); + } + /** + * @inheritDoc + */ + public function firstKey () { + return $this->keyFromOffset(0); + } + + /** + * @inheritDoc + */ + public function last () { + return $this->getFromOffset($this->count() - 1); + } + /** + * @inheritDoc + */ + public function lastKey () { + return $this->keyFromOffset($this->count() - 1); + } /** * @inheritDoc */ public function slice (int $offset, ?int $length = null): static { + $this->_checkOffset($offset); + $output = new static(); $currentIndex = 0;