{"id":21236670,"url":"https://github.com/maxg87/collectiondict","last_synced_at":"2026-02-09T23:33:30.102Z","repository":{"id":228873375,"uuid":"775129112","full_name":"MaxG87/collectiondict","owner":"MaxG87","description":"Create dictionaries that collect values into collections","archived":false,"fork":false,"pushed_at":"2025-02-16T14:35:35.000Z","size":240,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-20T21:42:13.852Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MaxG87.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,"zenodo":null}},"created_at":"2024-03-20T20:15:02.000Z","updated_at":"2025-02-16T14:35:26.000Z","dependencies_parsed_at":"2025-05-27T07:41:20.454Z","dependency_job_id":"63bf0367-15e8-49c9-8b73-627669352399","html_url":"https://github.com/MaxG87/collectiondict","commit_stats":null,"previous_names":["maxg87/collectiondict"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/MaxG87/collectiondict","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxG87%2Fcollectiondict","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxG87%2Fcollectiondict/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxG87%2Fcollectiondict/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxG87%2Fcollectiondict/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MaxG87","download_url":"https://codeload.github.com/MaxG87/collectiondict/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxG87%2Fcollectiondict/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29285817,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-09T21:57:15.303Z","status":"ssl_error","status_checked_at":"2026-02-09T21:57:11.537Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2024-11-21T00:13:14.127Z","updated_at":"2026-02-09T23:33:30.088Z","avatar_url":"https://github.com/MaxG87.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# collectiondict - Create multidicts more easily\n\nThis package simplifies the creation of multimappings from various sources. It\nprovides the functions `collectiondict`, `reverse_mapping` and\n`reverse_multimapping`. In all three cases the return value will be of type\n`dict[_ValueT, Collection[_KeyT]]`.\n\nAll three functions expect the target collection to be provided as an argument.\nThe supported collections are fixed. Only the built-in collections `Counter`,\n`frozenset`, `list`, `set`, and `tuple` as well as their subclasses are\nsupported. If a unsupported collection is passed, an exception is raised.\nHowever, `mypy` will warn about it.\n\nDue to the limits of Pythons type annotations, it is not possible to specify\nthe correct return type for the custom classes. Thus, custom classes are\nsupported but the return type is inferred to be the parent class (e.g. `set`),\nas opposed to be the class passed in (e.g. `class MySet(set)`).\n\nIn order to have the best type inference, it is recommended to **cast** `clct_t`\nto specify the value type. Passing a specialised collection class is **not**\nsupported currently. The examples show how to use a cast.\n\n\n## collectiondict\n\nGiven any stream of key-value tuples, this function creates a multi-dictionary\nwhich maps all values to the corresponding key. Thus, `collectiondict(clct,\nstream)` is similar to `dict(stream)` but does not discard values. It is\nconceptually similar to [multidict](https://pypi.org/project/multidict/) but\nmuch broader with respect to the supported types.\n\nThe implementation tries to be memory efficient and performant. It is possible\nto use it on extremely large streams, as long as the end result fits in memory.\nThus, if a list of the stream consumes more than half of the available memory,\n`collectiondict` can still be used. For deduplicating collections, e.g. `set`,\nthe stream could exceed available memory, as long as the key-value pairs do\nnot. One of the examples covers this scenario.\n\nSimple usage using `set`:\n\n    \u003e\u003e\u003e from collectiondict import collectiondict\n    \u003e\u003e\u003e collectiondict(set, [(\"a\", 1), (\"b\", 2), (\"a\", 3)])\n    {'a': {1, 3}, 'b': {2}}\n\nUsage using `frozenset` and a cast to have the best type inference:\n\n    \u003e\u003e\u003e import typing as t\n    \u003e\u003e\u003e from collectiondict import collectiondict\n    \u003e\u003e\u003e clct = t.cast(t.Type[frozenset[int]], frozenset)\n    \u003e\u003e\u003e collectiondict(clct, [(\"a\", 1), (\"b\", 2), (\"a\", 3)])\n    {'a': frozenset({1, 3}), 'b': frozenset({2})}\n\nScenario that might exceed memory:\n\n    \u003e\u003e\u003e from collectiondict import collectiondict\n    \u003e\u003e\u003e N=1000  # could be humongous, e.g. 10**20\n    \u003e\u003e\u003e collectiondict(set, ((str(n%2), n%3) for n in range(N)))\n    {'0': {0, 1, 2}, '1': {0, 1, 2}}\n\n\n## reverse_mapping\n\nGiven a mapping, e.g. a dictionary, from keys to values, this function reverses\nthe mapping so it maps from values to keys. The keys are collected in a\ncollection specified by passing a constructor as argument.\n\nSimple usage using `set`:\n\n    \u003e\u003e\u003e from collectiondict import reverse_mapping\n    \u003e\u003e\u003e reverse_mapping(set, {1: \"foobar\", 2: \"blablubb\", 3: \"foobar\"})\n    {'foobar': {1, 3}, 'blablubb': {2}}\n\nUsage using `frozenset` and a cast to have the best type inference:\n\n    \u003e\u003e\u003e import typing as t\n    \u003e\u003e\u003e from collectiondict import reverse_mapping\n    \u003e\u003e\u003e clct = t.cast(t.Type[frozenset[int]], frozenset)\n    \u003e\u003e\u003e reverse_mapping(clct, {1: \"foobar\", 2: \"blablubb\", 3: \"foobar\"})\n    {'foobar': frozenset({1, 3}), 'blablubb': frozenset({2})}\n\n\n## reverse_multimapping\n\nGiven a mapping, e.g. a dictionary, from keys to an iterable of values, this\nfunction reverses the mapping so it maps from values to keys. The keys are\ncollected in a collection specified by passing a constructor as argument.\n\nSimple usage using `set`:\n\n    \u003e\u003e\u003e from collectiondict import reverse_multimapping\n    \u003e\u003e\u003e reverse_multimapping(set, {1: \"abc\", 2: \"bcd\", 3: \"a\"})\n    {'a': {1, 3}, 'b': {1, 2}, 'c': {1, 2}, 'd': {2}}\n\nUsage using `frozenset` and a cast to have the best type inference:\n\n    \u003e\u003e\u003e import typing as t\n    \u003e\u003e\u003e from collectiondict import reverse_multimapping\n    \u003e\u003e\u003e clct = t.cast(t.Type[frozenset[int]], frozenset)\n    \u003e\u003e\u003e reverse_multimapping(clct, {1: [13, 37], 2: [13, 42], 3: [42]})\n    {13: frozenset({1, 2}), 37: frozenset({1}), 42: frozenset({2, 3})}\n\nSince `reverse_multimapping` is its own inverse, there is also a nice roundtrip\nbehaviour:\n\n    \u003e\u003e\u003e import typing as t\n    \u003e\u003e\u003e from collectiondict import reverse_multimapping\n    \u003e\u003e\u003e start = {1: {13, 37}, 2: {13, 42}, 3: {42}}\n    \u003e\u003e\u003e reversed_ = reverse_multimapping(set, start)\n    \u003e\u003e\u003e roundtripped = reverse_multimapping(set, reversed_)\n    \u003e\u003e\u003e start == roundtripped\n    True\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxg87%2Fcollectiondict","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaxg87%2Fcollectiondict","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxg87%2Fcollectiondict/lists"}