{"id":18323644,"url":"https://github.com/mortalflesh/mfcollectionsphp","last_synced_at":"2025-06-30T16:32:50.594Z","repository":{"id":10736428,"uuid":"60403734","full_name":"MortalFlesh/MFCollectionsPHP","owner":"MortalFlesh","description":"Collections for PHP - It's basically a syntax sugar over classic array structure, which allows you to use it as classic array, but adds some cool features.","archived":false,"fork":false,"pushed_at":"2024-09-27T07:55:05.000Z","size":638,"stargazers_count":10,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-21T13:45:23.687Z","etag":null,"topics":["collection","collections","generic","generic-collections","generic-interface","immutable","immutable-collections","mutable-collections","php","syntax-sugar"],"latest_commit_sha":null,"homepage":"https://github.com/MortalFlesh/MFCollectionsPHP/wiki","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MortalFlesh.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-06-04T09:50:22.000Z","updated_at":"2023-04-12T07:47:24.000Z","dependencies_parsed_at":"2023-11-28T12:30:05.545Z","dependency_job_id":"bf630b85-a235-4a91-8cd1-c6b581828950","html_url":"https://github.com/MortalFlesh/MFCollectionsPHP","commit_stats":{"total_commits":173,"total_committers":1,"mean_commits":173.0,"dds":0.0,"last_synced_commit":"bed1eed8906ca089f521044b4d083cd867968bff"},"previous_names":[],"tags_count":45,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MortalFlesh%2FMFCollectionsPHP","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MortalFlesh%2FMFCollectionsPHP/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MortalFlesh%2FMFCollectionsPHP/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MortalFlesh%2FMFCollectionsPHP/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MortalFlesh","download_url":"https://codeload.github.com/MortalFlesh/MFCollectionsPHP/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247415783,"owners_count":20935383,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["collection","collections","generic","generic-collections","generic-interface","immutable","immutable-collections","mutable-collections","php","syntax-sugar"],"created_at":"2024-11-05T18:28:59.444Z","updated_at":"2025-04-05T23:31:31.868Z","avatar_url":"https://github.com/MortalFlesh.png","language":"PHP","readme":"MFCollections for PHP\n=====================\n\n[![Latest Stable Version](https://img.shields.io/packagist/v/mf/collections-php.svg)](https://packagist.org/packages/mf/collections-php)\n[![Total Downloads](https://img.shields.io/packagist/dt/mf/collections-php.svg)](https://packagist.org/packages/mf/collections-php)\n[![License](https://img.shields.io/packagist/l/mf/collections-php.svg)](https://packagist.org/packages/mf/collections-php)\n[![Tests and linting](https://github.com/MortalFlesh/MFCollectionsPHP/actions/workflows/tests.yaml/badge.svg)](https://github.com/MortalFlesh/MFCollectionsPHP/actions/workflows/tests.yaml)\n[![Coverage Status](https://coveralls.io/repos/github/MortalFlesh/MFCollectionsPHP/badge.svg?branch=master)](https://coveralls.io/github/MortalFlesh/MFCollectionsPHP?branch=master)\n\nIt's basically a syntax sugar over classic array structure, which allows you to use it as classic array, but adds some cool features.\n\n## Table of Contents\n- [Installation](#installation)\n- [Requirements](#requirements)\n- [Base Interfaces](#base-interfaces)\n    - [IEnumerable](#enumerable-interface)\n    - [ICollection](#collection-interface)\n    - [IList](#list-interface)\n    - [IMap](#map-interface)\n    - [ISeq](#seq-interface)\n    - [ITuple](#tuple-interface)\n- [Mutable](#mutable-collections)\n- [Immutable](#immutable-collections)\n- [Generic](#generic-collections)\n- [Arrow Functions](#arrow-functions)\n- [Plans for next versions](#plans-for-next-versions)\n\n\n## Installation\n```bash\ncomposer require mf/collections-php\n```\n\n\n## Requirements\n- `PHP ^8.2`\n\n\n## Base Interfaces\n\nCheck out [Documentation](https://github.com/MortalFlesh/MFCollectionsPHP/wiki) for more details.\n\n### \u003ca name=\"enumerable-interface\"\u003e\u003c/a\u003eIEnumerable\n- basic Interface for enumerable\n- extends `IteratorAggregate`, `Countable`\n- [see Immutable tuple](#immutable-tuple)\n- [see Mutable PrioritizedCollection](#mutable-prioritized-collection)\n\n### \u003ca name=\"collection-interface\"\u003e\u003c/a\u003eICollection\n- basic Interface for Collections\n- extends `IEnumerable`\n- [see Mutable collections](#mutable-collections)\n- [see Immutable collections](#immutable-collections)\n\n### \u003ca name=\"list-interface\"\u003e\u003c/a\u003eIList\nA _list_ is an ordered (_possibly immutable_) series of elements of the same type.\n- extends `ICollection`\n- [see Mutable list](#mutable-list)\n- [see Immutable list](#immutable-list)\n\n### \u003ca name=\"map-interface\"\u003e\u003c/a\u003eIMap\nA _map_ is an ordered (_possibly immutable_) series of key values pairs.\n- extends `ICollection, ArrayAccess`\n- [see Mutable map](#mutable-map)\n- [see Immutable map](#immutable-map)\n\n### \u003ca name=\"seq-interface\"\u003e\u003c/a\u003eISeq\nA _sequence_ is a logical series of elements all of one type.\n- extends `ICollection`\n- [see Immutable seq](#immutable-seq)\n\n### \u003ca name=\"tuple-interface\"\u003e\u003c/a\u003eITuple\nA _tuple_ is a grouping of unnamed but ordered values, possibly of different types.\n- extends `IEnumerable`, `ArrayAccess`, `Stringable`\n- [see Immutable tuple](#immutable-tuple)\n\n\n## \u003ca name=\"mutable-collections\"\u003e\u003c/a\u003eMutable Collections\n\n### Interfaces\n- `Mutable\\Generic\\ICollection`, `Mutable\\Generic\\IList`, `Mutable\\Generic\\IMap`\n\n### Mutable\\Generic\\ListCollection\n- implements `Mutable\\Generic\\IList`\n- is `eager` as possible\n\n### Mutable\\Generic\\Map\n- implements `Mutable\\Generic\\IMap`\n- is `eager` as possible\n\n### \u003ca name=\"mutable-prioritized-collection\"\u003e\u003c/a\u003eMutable\\Generic\\PrioritizedCollection\n- implements `IEnumerable`\n- holds items with `generic` type by `priority`\n- is `eager` as possible\n\n#### Example of strategies by priority\nFor case when you want to apply `only the first strategy` which can do what you want.\nYou can add strategies `dynamically` and still apply them `by priority` later. \n```php\n// initialization of strategies\n/** @phpstan-var PrioritizedCollection\u003cStrategyInterface\u003e $strategies */\n$strategies = new PrioritizedCollection();\n$strategies-\u003eadd(new DefaultStrategy(), 1);\n\n// added later\n$strategies-\u003eadd(new SpecialStrategy(), 100);\n\n// find and apply first suitable strategy\n/** @var StrategyInterface $strategy */\nforeach ($strategies as $strategy) {\n    if ($strategy-\u003esupports($somethingStrategic)) {\n        return $strategy-\u003eapply($somethingStrategic);\n    }\n}\n```\n\n\n## \u003ca name=\"immutable-collections\"\u003e\u003c/a\u003eImmutable Collections\n- `internal state` of Immutable\\Collection instance will `never change` from the outside (it is `readonly`)\n```php\n$list = new Immutable\\ListCollection();\n$listWith1 = $list-\u003eadd(1);\n\n// $list != $listWith1\necho $list-\u003ecount();        // 0\necho $listWith1-\u003ecount();   // 1\n```\n- `$list` is still an empty `Immutable\\ListCollection`\n- `$listWith1` is new instance of `Immutable\\ListCollection` with value `1` \n\n### Interfaces\n- `Immutable\\Generic\\ICollection`, `Immutable\\Generic\\IList`, `Immutable\\Generic\\IMap`, `Immutable\\Generic\\ISeq`, `Immutable\\ITuple`\n\n### \u003ca name=\"immutable-list\"\u003e\u003c/a\u003eImmutable\\Generic\\ListCollection\n- implements `Immutable\\Generic\\IList`\n- is `eager` as possible\n\n### \u003ca name=\"immutable-map\"\u003e\u003c/a\u003eImmutable\\Generic\\Map\n- implements `Immutable\\Generic\\IMap`\n- is `eager` as possible\n\n### \u003ca name=\"immutable-seq\"\u003e\u003c/a\u003eImmutable\\Seq\n- implements `Immutable\\Generic\\ISeq`\n- is `lazy` as possible (_even could be `Infinite`_)\n```php\n$seq = Seq::infinite()                         // 1, 2, ...\n    -\u003efilter(fn ($i) =\u003e $i % 2 === 0)   // 2, 4, ...\n    -\u003eskip(2)                           // 6, 8, ...\n    -\u003emap(fn ($i) =\u003e $i * $i)           // 36, 64, ...\n    -\u003etakeWhile(fn ($i) =\u003e $i \u003c 100)    // 36, 64\n    -\u003ereverse()                         // 64, 36\n    -\u003etake(1);                          // 64\n// for now the Sequence is still lazy\n\n// this will generate (evaluate) the values\n$array = $seq-\u003etoArray();               // [64]\n```\n\n### \u003ca name=\"immutable-kvpair\"\u003e\u003c/a\u003eImmutable\\Generic\\KVPair\n- always has a `Key` and the `Value`\n- key is restricted to `int|string` so it may be used in the `foreach` as a key\n- can contain any values\n\n### \u003ca name=\"immutable-tuple\"\u003e\u003c/a\u003eImmutable\\Tuple\n- implements `Immutable\\ITuple`\n- must have at least 2 values (_otherwise it is just a single value_)\n- is `eager` as possible\n- allows `destructuring`, `matching` and `parsing`/`formatting`\n- can contain any scalar values and/or arrays\n    - in string representation of a `Tuple`, array values must be separated by `;` (_not by `,`_)\n\n#### Parsing\n```php\nTuple::parse('(foo, bar)')-\u003etoArray();                  // ['foo', 'bar']\nTuple::parse('(\"foo, bar\", boo)')-\u003etoArray();           // ['foo, bar', 'boo']\nTuple::parse('(1, \"foo, bar\", true)')-\u003etoArray();       // [1, 'foo, bar', true]\nTuple::parse('(1, [2; 3], [four; \"Five\"])')-\u003etoArray(); // [1, [2, 3], ['four', 'five']]\n```\n\n#### Matching and comparing\n```php\nTuple::from([1, 1])-\u003ematch('int', 'int');                      // true\nTuple::from([1, 2, 3])-\u003eisSame(Tuple::of(1, 2, 3));            // true\nTuple::of(10, 'Foo', null)-\u003ematch('int', 'string', '?string'); // true\nTuple::of(10, [9, 8])-\u003ematch('int', 'array');                  // true\n```\n\n#### Parsing and matching\n```php\nTuple::parseMatch('(foo, bar)', 'string', 'string')-\u003etoArray();        // ['foo', 'bar']\nTuple::parseMatchTypes('(foo, bar)', ['string', 'string'])-\u003etoArray(); // ['foo', 'bar']\n\n// invalid types\nTuple::parseMatch('(foo, bar, 1)', 'string', 'string'); // throws \\InvalidArgumentException \"Given tuple does NOT match expected types (string, string) - got (string, string, int)\"\n```\n\n#### Formatting\n```php\nTuple::from([1, 'foo', null])-\u003etoString();          // '(1, \"foo\", null)'\n\n// for URL\nTuple::from(['foo', 'bar'])-\u003etoStringForUrl();      // '(foo,bar)'\nTuple::from(['foo-bar', 'boo'])-\u003etoStringForUrl();  // '(foo-bar,bar)'\nTuple::from(['mail', 'a@b.com'])-\u003etoStringForUrl(); // '(mail,\"a@b.com\")'\n```\n\n#### Destructuring\n```php\n$tuple  = Tuple::of('first', 2, 3); // ('first', 2, 3)\n$first  = $tuple-\u003efirst();          // 'first'\n$second = $tuple-\u003esecond();         // 2\n[$first, $second] = $tuple;         // $first = 'first'; $second = 2\n[,, $third]       = $tuple;         // 3\n```\n\n#### Unpacking\n```php\nsprintf('Title: %s | Value: %s', ...Tuple::of('foo', 'bar')); // \"Title: foo | Value: bar\"\n```\n\n#### Merging\n- merging `Tuples` will automatically flat them (_see last example below_)\n```php\n$base  = Tuple::of('one', 'two');                       // ('one', 'two')\n$upTo3 = Tuple::merge($base, 'three');                  // ('one', 'two', 'three')\n$upTo4 = Tuple::merge($base, '3', 'four');              // ('one', 'two', '3', 'four')\n$upTo5 = Tuple::merge($base, ['3', '4'], '5');          // ('one', 'two', ['3', '4'], '5')\n$upTo5 = Tuple::merge($base, Tuple::of('3', '4'), '5'); // ('one', 'two', '3', '4', '5')\n```\n\n#### Merging and matching\n```php\n$base = Tuple::of('one', 'two');                                    // ('one', 'two')\n$upTo3 = Tuple::mergeMatch(['string', 'string', 'int'], $base, 3);  // ('one', 'two', 3)\n\n// invalid types\nTuple::mergeMatch(['string', 'string'], $base, 3); // throws \\InvalidArgumentException \"Merged tuple does NOT match expected types (string, string) - got (string, string, int).\"\n```\n\n\n## Sequences and lazy mapping\n- if your `Sequence` get mapped and filtered many times (_for readability_), it is not a problem\n    - `map -\u003e map -\u003e filter -\u003e map -\u003e filter -\u003e map` will iterate the collection **only once** (_for applying all modifiers at once_)\n    - this modification is done when another method is triggered, so adding new modifier is an **atomic** operation\n- all the values are generated on the fly, so it may end on out of memory exception\n\n\n## Plans for next versions\n- use `Symfony/Stopwatch` in unit tests\n- _even better_ documentation ([current](https://github.com/MortalFlesh/MFCollectionsPHP/wiki))\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmortalflesh%2Fmfcollectionsphp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmortalflesh%2Fmfcollectionsphp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmortalflesh%2Fmfcollectionsphp/lists"}