Skip to content

micoli/multitude

Repository files navigation

Micoli\Multitude

A collection library for PHP.

Build Status Coverage Status Latest Stable Version Total Downloads Latest Unstable Version License PHP Version Require

Two types of collections are available:

  • Sets (MutableSet and ImmutableSet), are sequences of unique values. Values can be of any types.
  • Map (MutableMap and ImmutableMap), 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

ImmutableMap MutableMap ImmutableSet MutableSet
__construct [x] [x] [x] [x]
append [x] [x]
apply [x] [x] [x] [x]
count [x] [x] [x] [x]
filter [x] [x] [x] [x]
first [x] [x] [x] [x]
forEach [x] [x] [x] [x]
fromIterable [x] [x]
get [x] [x] [x] [x]
getIterator [x] [x] [x] [x]
getTuples [x] [x]
hasIndex [x] [x]
hasKey [x] [x]
hasValue [x] [x] [x] [x]
indexDiff [x] [x]
indexIntersect [x] [x]
isEmpty [x] [x] [x] [x]
keyDiff [x] [x]
keyIntersect [x] [x]
keys [x] [x] [x] [x]
ImmutableMap MutableMap ImmutableSet MutableSet
last [x] [x] [x] [x]
map [x] [x] [x] [x]
offsetExists [x] [x]
offsetGet [x] [x]
offsetSet [x] [x]
offsetUnset [x] [x]
reduce [x] [x] [x] [x]
remove [x] [x]
removeKey [x] [x]
removeValue [x] [x]
set [x] [x]
slice [x] [x] [x] [x]
sort [x] [x] [x] [x]
toArray [x] [x] [x] [x]
toImmutable [x] [x]
toMutable [x] [x]
valueDiff [x] [x] [x] [x]
valueIntersect [x] [x] [x] [x]
values [x] [x] [x] [x]

AbstractSet

AbstractMap

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

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published