micoli / multitude
Requires
- php: >=8.1
- loophp/collection: ^7.1
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.16
- phan/phan: ^5.4
- php-coveralls/php-coveralls: ^0.1.0
- phpdocumentor/reflection-docblock: ^5.3
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0
- ramsey/uuid: ^4.7
- symfony/var-dumper: ^6.2
- vimeo/psalm: ^5.9
README
A collection library for PHP.
Two types of collections are available:
- Sets (
MutableSet
andImmutableSet
), are sequences of unique values. Values can be of any types. - Map (
MutableMap
andImmutableMap
), is a sequential collection of key-value pairs. Keys can be any type, but must be unique. Values can be of any types.
In both ImmutableSet
and ImmutableMap
, if a method alter the content af the inner values, a new instance is returned of the same type. Oppositely, in MutableSet
and MutableMap
, same methods are altering the inner values.
Methods are the most possible fluent.
Thanks to https://github.com/BenMorel for the main ideas used in that library, this is a complete rewrite of it's initial version.
Installation
This library is installable via Composer:
composer require micoli/multitude
Requirements
This library requires PHP 8.0 or later.
Project status
While this library is still under development, it is still in early development status. It follows semver version tagging.
Quick start
Constructor are only statics:
new MutableSet(['a','b','c'])
new MutableMap([2=>'a',3=>'b',4=>'c'])
new MutableMap([[2,'a'],['2','aa'],[3,'b'],['3','bb'],[4,'c'])
You can use fromTuples constructor if you need a strong typing for keys of your map, e.g. '2'
key is different of 2
.
Methods that accept a bool $throw
parameter will trigger an exception if $throw == true
or fails silently if $throw == false
.
Example with an associative array
public function testItShouldFullyWorkWithAssociativeArray(): void { /** @var ImmutableMap<string, array{value:int,tags:list<string>}> $map */ $map = new ImmutableMap([ ['library', ['value' => 10, 'tags' => ['tag1']]], ['projects', ['value' => 5, 'tags' => ['tag2']]], ['gist', ['value' => 7, 'tags' => ['tag1', 'tag2']]], ['repository', ['value' => 7, 'tags' => ['tag3']]], ]); $totalSum = $map ->filter(fn (array $project, mixed $category): bool => array_search('tag1', $project['tags']) !== false) ->reduce(fn (int $sum, mixed $project, mixed $category): int => $sum + $project['value'], 0); self::assertSame($totalSum, 17); self::assertCount(4, $map); }
Example with an immutable map fully typed
File: Project.php
<?php declare(strict_types=1); namespace Micoli\Multitude\Tests\Fixtures; class Project { public function __construct( public readonly int $value, public readonly Tags $tags, ) { } }
File: Tags
<?php declare(strict_types=1); namespace Micoli\Multitude\Tests\Fixtures; use Micoli\Multitude\Set\ImmutableSet; /** * @extends ImmutableSet<string> */ class Tags extends ImmutableSet { }
File: Projects
<?php declare(strict_types=1); namespace Micoli\Multitude\Tests\Fixtures; use Micoli\Multitude\Map\ImmutableMap; /** * @extends ImmutableMap<string, Project> */ class Projects extends ImmutableMap { /** * Add or replace a value in the map */ public function improvedSet(string $newKey, Project $newValue): static { // do specific stuff, like logging or ther return $this->set($newKey, $newValue); } }
public function testItShouldFullyWorkWithObjects(): void { $map = new Projects([ ['library', new Project(10, new Tags(['tag1']))], ['projects', new Project(5, new Tags(['tag2']))], ['gist', new Project(7, new Tags(['tag1', 'tag2']))], ['repository', new Project(7, new Tags(['tag3']))], ]); $totalSum = $map ->filter(fn (Project $project, mixed $category): bool => $project->tags->hasValue('tag1')) ->reduce(fn (int $sum, Project $project, mixed $category): int => $sum + $project->value, 0); self::assertInstanceOf( Projects::class, $map->filter(fn (Project $project, mixed $category): bool => true), ); self::assertSame($totalSum, 17); self::assertCount(4, $map); $newMap = $map->improvedSet('NewType', new Project(10, new Tags(['tag4']))); self::assertCount(5, $newMap); }
Available verbs
Verb parity
AbstractSet
- __construct
- append
- apply
- count
- filter
- first
- forEach
- get
- getIterator
- hasIndex
- hasValue
- indexDiff
- indexIntersect
- isEmpty
- keys
- last
- map
- reduce
- remove
- slice
- sort
- toArray
- valueDiff
- valueIntersect
- values
AbstractMap
- __construct
- apply
- count
- filter
- first
- forEach
- fromIterable
- get
- getIterator
- getTuples
- hasKey
- hasValue
- isEmpty
- keyDiff
- keyIntersect
- keys
- last
- map
- offsetExists
- offsetGet
- offsetSet
- offsetUnset
- reduce
- removeKey
- removeValue
- set
- slice
- sort
- toArray
- valueDiff
- valueIntersect
- values
AbstractSet
AbstractSet::__construct
public function __construct(iterable $values = [])
AbstractSet::append
public function append(mixed $newValue, bool $throw = true): static
Append a value at the end of the set
AbstractSet::apply
public function apply(callable $callable): static
Replace all values by applying a callback to the current instance
AbstractSet::count
public function count(): int
return the number of items in the set
AbstractSet::filter
public function filter(callable $callable): static
Filter the set using a callback function
AbstractSet::first
public function first(bool $throw = true): mixed
Return the first value in the set
AbstractSet::forEach
public function forEach(callable $callable): static
Apply a callback on set values
Callback receive $value
and $index
AbstractSet::get
public function get(int $index, mixed $defaultValue = null): mixed
Return a value in the set by index
AbstractSet::getIterator
public function getIterator(): Traversable
Return an iterator for values
AbstractSet::hasIndex
public function hasIndex(int $index): bool
Return if a set contains an index
AbstractSet::hasValue
public function hasValue(mixed $searchedValue): bool
Return if a set contains a value
AbstractSet::indexDiff
public function indexDiff(AbstractSet $compared): static
Return a set of all items where keys are not in argument set
AbstractSet::indexIntersect
public function indexIntersect(AbstractSet $compared): static
Return a map of all items where keys are in arguments map
AbstractSet::isEmpty
public function isEmpty(): bool
Return if a set is empty
AbstractSet::keys
public function keys(): Generator
Return an iterator of keys
AbstractSet::last
public function last(bool $throw = true): mixed
Return the latest value in the set
AbstractSet::map
public function map(callable $callable)
Applies the callback to the values, keys are preserved
Callback receive $value
and $index
AbstractSet::reduce
public function reduce(callable $callable, mixed $accumulator): mixed
Iteratively reduce the Set to a single value using a callback function
Callback receive $accumulator
,$value
and $index
AbstractSet::remove
public function remove(mixed $searchedValue, bool $throw = true): static
Remove a value in the set
AbstractSet::slice
public function slice(int $offset, ?int $length = null): static
Extract a slice of the set
AbstractSet::sort
public function sort(callable $callable): static
Sort the map using a callback function
callback is of callable (TValue, TValue, int, int): int
and must return -1,0,1 as spaceship operator
AbstractSet::toArray
public function toArray(): array
Return an array representing the values
AbstractSet::valueDiff
public function valueDiff(AbstractSet $compared): static
Return a Set of all items where values are not in argument set
AbstractSet::valueIntersect
public function valueIntersect(AbstractSet $compared): static
Return a set of all items where values are in argument set
AbstractSet::values
public function values(): Generator
Return an iterator of values
AbstractMap
AbstractMap::__construct
public function __construct(array $tuples = [])
AbstractMap::apply
public function apply(callable $callable): static
Replace all values by applying a callback to the current instance
AbstractMap::count
public function count(): int
Return the number of items in the map
AbstractMap::filter
public function filter(callable $callable): static
Filter the map using a callback function
AbstractMap::first
public function first(bool $throw = true): mixed
Return the first value in the map
AbstractMap::forEach
public function forEach(callable $callable): static
Apply a callback on set values
AbstractMap::fromIterable
public static function fromIterable(iterable $values): static
Return a new instance from an array.
AbstractMap::get
public function get(mixed $searchedKey, mixed $defaultValue = null): mixed
Return a value in the map by index
AbstractMap::getIterator
public function getIterator(): Traversable
Return an iterator for values by keys
AbstractMap::getTuples
public function getTuples(): array
AbstractMap::hasKey
public function hasKey(mixed $searchedKey): bool
Return if a map contains a specific key
AbstractMap::hasValue
public function hasValue(mixed $searchedValue): bool
Return if a map contains a specific value
AbstractMap::isEmpty
public function isEmpty(): bool
Return if a map is empty
AbstractMap::keyDiff
public function keyDiff(AbstractMap $compared): static
Return a map of all items where keys are not in argument map
AbstractMap::keyIntersect
public function keyIntersect(AbstractMap $compared): static
Return a map of all items where keys are in arguments map
AbstractMap::keys
public function keys(): Generator
Return an iterator of keys
AbstractMap::last
public function last(bool $throw = true): mixed
Return the latest value in the map
AbstractMap::map
public function map(callable $callable)
Applies the callback to the values, keys are preserved
AbstractMap::offsetExists
public function offsetExists(mixed $offset): bool
AbstractMap::offsetGet
public function offsetGet(mixed $offset): mixed
AbstractMap::offsetSet
public function offsetSet(mixed $offset, mixed $value): void
AbstractMap::offsetUnset
public function offsetUnset(mixed $offset): void
AbstractMap::reduce
public function reduce(callable $callable, mixed $accumulator): mixed
Iteratively reduce the Map to a single value using a callback function
Callback receive $accumulator
,$value
and $key
AbstractMap::removeKey
public function removeKey(mixed $searchedKey): static
Remove a value in the map by key
AbstractMap::removeValue
public function removeValue(mixed $searchedValue): static
Remove a value in the map by value
AbstractMap::set
public function set(mixed $newKey, mixed $newValue): static
Add or replace a value in the map
AbstractMap::slice
public function slice(int $offset, ?int $length = null): static
Extract a slice of the map
AbstractMap::sort
public function sort(callable $callable): static
Sort the map using a callback function
callback is of callable(TValue, TValue, TKey, TKey, int, int): int
and must return -1,0,1 as spaceship operator
AbstractMap::toArray
public function toArray(): array
Return an array representing the values
AbstractMap::valueDiff
public function valueDiff(AbstractMap $compared): static
Return a map of all items where values are not in arguments map
AbstractMap::valueIntersect
public function valueIntersect(AbstractMap $compared): static
Return a map of all items where values are in argument map
AbstractMap::values
public function values(): Generator
Return an iterator of values