Skip to content

Instantly share code, notes, and snippets.

@ghostwriter
Created June 22, 2022 00:45
Show Gist options
  • Select an option

  • Save ghostwriter/50a2ee58efe7354a5591abad739ff772 to your computer and use it in GitHub Desktop.

Select an option

Save ghostwriter/50a2ee58efe7354a5591abad739ff772 to your computer and use it in GitHub Desktop.
collection wip
<?php
declare(strict_types=1);
namespace Ghostwriter\Collection;
use Closure;
use Ghostwriter\Collection\Contract\CollectionInterface;
use InvalidArgumentException;
use Traversable;
use const PHP_INT_MAX;
use const true;
/**
* @template TValue
* @implements CollectionInterface<TValue>
*/
abstract class AbstractCollection implements CollectionInterface
{
/**
* @var Closure():iterable<TValue>
*/
private Closure $generator;
/**
* @param ?Closure():iterable<TValue> $generator
*/
final private function __construct(?Closure $generator = null)
{
$this->generator = $generator ?? static fn (): iterable => [];
}
public function append(iterable $iterable = []): self
{
return new static(function () use ($iterable): Traversable {
yield from ($this->generator)();
yield from $iterable;
});
}
public function contains(mixed $item, ?Closure $function = null): bool
{
$function = $function ?? static fn (mixed $value): mixed => $value;
foreach (($this->generator)() as $value) {
if ($function($item) === $function($value)) {
return true;
}
}
return false;
}
public function count(): int
{
return iterator_count($this);
}
public function drop(int $length): self
{
return $this->slice($length);
}
public function filter(Closure $function): self
{
return new static(function () use ($function): Traversable {
foreach (($this->generator)() as $value) {
if ($function($value)) {
yield $value;
}
}
});
}
public function first(Closure $function = null): mixed
{
$function = $function ?? static fn (mixed $_): bool => true;
foreach (($this->generator)() as $value) {
if ($function($value)) {
return $value;
}
}
return null;
}
public static function fromGenerator(Closure $generator): self
{
return new static($generator);
}
public static function fromIterable(iterable $values = []): self
{
return new static(static fn () => yield from $values);
}
public function getIterator(): iterable
{
return ($this->generator)();
}
public function isEmpty(): bool
{
return 0 === $this->count();
}
public function last(?Closure $function = null): mixed
{
$last = null;
$function = $function ?? static fn (mixed $_): bool => true;
foreach (($this->generator)() as $value) {
if ($function($value)) {
$last = $value;
}
}
return $last;
}
public function map(Closure $function): self
{
return new static(function () use ($function) {
foreach (($this->generator)() as $value) {
yield $function($value);
}
});
}
public function reduce(Closure $function, mixed $accumulator = null): mixed
{
foreach (($this->generator)() as $value) {
$accumulator = $function($accumulator, $value);
}
return $accumulator;
}
public function slice(int $offset, int $length = PHP_INT_MAX): self
{
return new static(function () use ($offset, $length): Traversable {
$count = 0;
if ($offset < $count) {
throw new InvalidArgumentException('$offset must be positive');
}
if ($length < $count) {
throw new InvalidArgumentException('$length must be positive');
}
if ($length === $count) {
return;
}
foreach (($this->generator)() as $value) {
if ($count++ < $offset) {
continue;
}
yield $value;
if ($count >= ($offset + $length)) {
break;
}
}
});
}
public function take(int $length): self
{
return $this->slice(0, $length);
}
public function toArray(): array
{
return iterator_to_array($this, false);
}
}
<?php
declare(strict_types=1);
namespace Ghostwriter\Collection\Contract;
use Closure;
use Countable;
use IteratorAggregate;
use Traversable;
use const PHP_INT_MAX;
/**
* @template TValue
* @template-implements IteratorAggregate<array-key,TValue>
*/
interface CollectionInterface extends Countable, IteratorAggregate
{
/**
* @param iterable<TValue> $iterable
*/
public function append(iterable $iterable): self;
/**
* @param TValue $item
* @param ?Closure(TValue):TValue $function
*/
public function contains(mixed $item, ?Closure $function = null): bool;
/**
* @return int<0, max>
*/
public function count(): int;
/**
* @param int<0, max> $length
*/
public function drop(int $length): self;
/**
* @param (Closure(TValue):bool) $function
*/
public function filter(Closure $function): self;
/**
* @param ?Closure(TValue):bool $function
*
* @return null|TValue
*/
public function first(?Closure $function = null): mixed;
/**
* @param Closure():Traversable $generator
*/
public static function fromGenerator(Closure $generator): self;
/**
* @template TIterableValue
*
* @param iterable<TIterableValue> $values
*/
public static function fromIterable(iterable $values = []): self;
/**
* @return iterable<TValue>
*/
public function getIterator(): iterable;
public function isEmpty(): bool;
/**
* @param ?Closure(TValue):bool $function
*
* @return null|TValue
*/
public function last(?Closure $function = null): mixed;
/**
* @template TMapValue
*
* @param (Closure(TValue):TMapValue) $function
*/
public function map(Closure $function): self;
/**
* @template TReduceValue
*
* @param (Closure(TReduceValue|null,TValue):TReduceValue) $function
* @param TReduceValue $accumulator
*
* @return null|TReduceValue
*/
public function reduce(Closure $function, mixed $accumulator = null): mixed;
/**
* @param int<0, max> $offset
* @param int<0, max> $length
*/
public function slice(int $offset, int $length = PHP_INT_MAX): self;
/**
* @param int<0, max> $length
*/
public function take(int $length): self;
/**
* @return array<array-key,TValue>
*/
public function toArray(): array;
}
@ghostwriter

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment