Skip to main content

richset

Python PyPI version Coverage License

richset interpolates with set, dict, and list.

Motivations and Concepts

  • richset provides useful functions for common use cases of set, dict, and list.
  • builtin set, dict and list requires many boilerplate codes.
  • Pure Python package.
  • Fully typing supported.
  • No magic. No meta-programming.

Install

$ pip install richset

Usage

from dataclasses import dataclass
from richset import RichSet


@dataclass(frozen=True)
class Something:
    id: int
    name: str


richset = RichSet.from_list([
    Something(1, 'one'),
    Something(2, 'two'),
    Something(3, 'three'),
])

Conversions

richset.to_list()  # => [Something(1, 'one'), Something(2, 'two'), Something(3, 'three')]
richset.to_tuple()  # => (Something(1, 'one'), Something(2, 'two'), Something(3, 'three'))
richset.to_set()  # => {Something(1, 'one'), Something(2, 'two'), Something(3, 'three')}
richset.to_frozenset()  # => frozenset({Something(1, 'one'), Something(2, 'two'), Something(3, 'three')})
richset.to_dict(lambda s: s.id)  # => {1: Something(1, 'one'), 2: Something(2, 'two'), 3: Something(3, 'three')}

to_dict() takes second argument duplicated which is a choice of 'error', 'first' or 'last'.

  • if duplicated is 'error', then to_dict() raises ValueError if there is a duplicated key.
  • if duplicated is 'first', then to_dict() picks the first one of duplicated key.
  • if duplicated is 'last', then to_dict() picks the last one of duplicated key.

to_dict_of_list() is similar to to_dict() but it returns a dict of list.

richset.to_dict_of_list(lambda s: s.name)  # => {'john': [Something(1, 'john'), Something(2, 'john')], 'jane': [Something(3, 'jane')]}

List accessors

richset.first()  # => returns first item `Something(1, 'one')` or raise Error (if empty)
richset.get_first()  # => returns first item `Something(1, 'one')` or None (if empty)
richset.last()  # => returns last item `Something(3, 'three')` or raise Error (if empty)
richset.get_last()  # => returns last item `Something(3, 'three')` or None (if empty)
richset.nth(2)  # => returns 3rd item `Something(3, 'three')` or raise Error (if empty)
richset.get_nth(2)  # => returns 3rd item `Something(3, 'three')` or None (if empty)
richset.one()  # => returns one item `Something(1, 'one')` or raise Error (if empty)
richset.get_one()  # => returns one item `Something(1, 'one')` or None (if empty)

Note: get_first, get_last, get_nth and get_one accept default argument that returns specified value instead of None.

richset.get_nth(100, default=Something(-1, 'default'))  # => Something(-1, 'default')

List basic manipulations

richset.pushed(Something(4, 'four')).to_list()  # => [Something(1, 'one'), Something(2, 'two'), Something(3, 'three'), Something(4, 'four')]
richset.unshifted(Something(4, 'four')).to_list()  # => [Something(4, 'four'), Something(1, 'one'), Something(2, 'two'), Something(3, 'three')]
richset.popped()  # => Something(3, 'three'), RichSet([Something(1, 'one'), Something(2, 'two')])
richset.shifted()  # => Something(1, 'one'), RichSet([Something(2, 'two'), Something(3, 'three')])
richset.slice(1, 2).to_list()  # => [Something(2, 'two')]
richset.divide_at(1)  # => RichSet([Something(1, 'one')]), RichSet([Something(2, 'two'), Something(3, 'three')])
  • pushed_all() and unshifted_all() are similar to pushed() and unshifted() but they accept multiple items.
  • popped_n() and shifted_n() are similar to popped() and shifted() but they accept count of items.

List functional manipulations

richset.unique(lambda s: s.id)  # => unique by id
richset.map(lambda s: s.id).to_list()  # => [1, 2, 3]
richset.filter(lambda s: s.id > 1).to_list()  # => [Something(2, 'two'), Something(3, 'three')]
richset.index_of(lambda s: s.id == 2)  # => 1
richset.indices_of(lambda s: s.id == 2)  # => [1]
richset.search_first(lambda s: s.id == 2)  # => Something(2, 'two')
richset.search_last(lambda s: s.id == 2)  # => Something(2, 'two')
richset.search_all(lambda s: s.id == 2)  # => [Something(2, 'two')]
richset.contains(lambda s: s.id == 2)  # => True
richset.has(Something(2, 'two'))  # => True

Sorts

richset.sorted(key=lambda s: s.name, reverse=True).to_list()  # => [Something(2, 'two'), Something(3, 'three'), Something(1, 'one')]
richset.reversed().to_list()  # => [Something(3, 'three'), Something(2, 'two'), Something(1, 'one')]

Statistics

richset.is_empty()  # => True if empty
richset.is_non_empty()  # => True if not empty
richset.size()  # => 3
richset.count(lambda s: s.id > 1)  # => 2

Set operations

richset = RichSet.from_list([
    Something(3, 'three'),
    Something(4, 'four'),
    Something(5, 'five'),
])
richset2 = RichSet.from_list([
    Something(3, 'three'),
    Something(4, 'four'),
    Something(6, 'six'),
])
richset.union(richset2).to_set()  # => {Something(3, 'three'), Something(4, 'four'), Something(5, 'five'), Something(6, 'six')}
richset.intersection(richset2).to_set()  # => {Something(3, 'three'), Something(4, 'four')}
richset.difference(richset2).to_set()  # => {Something(5, 'five')}
richset.symmetric_difference(richset2).to_set()  # => {Something(5, 'five'), Something(6, 'six')}
richset.cartesian_product(richset2).to_set()  # => {(Something(3, 'three'), Something(3, 'three')), (Something(3, 'three'), Something(4, 'four')), (Something(3, 'three'), Something(6, 'six')), (Something(4, 'four'), Something(3, 'three')), (Something(4, 'four'), Something(4, 'four')), (Something(4, 'four'), Something(6, 'six')), (Something(5, 'five'), Something(3, 'three')), (Something(5, 'five'), Something(4, 'four')), (Something(5, 'five'), Something(6, 'six'))}
richset.zip(richset2).to_set()  # => {(Something(3, 'three'), Something(3, 'three')), (Something(4, 'four'), Something(4, 'four')), (Something(5, 'five'), Something(6, 'six')}

Also is_subset(), is_superset(), is_disjoint(), is_equal_as_set() and zip_longest() are available.

Grouping

richset.group_by(lambda item: item.id % 2)  # => {1: RichSet(records=(Something(id=1, name='one'), Something(id=3, name='three'))), 0: RichSet(records=(Something(id=2, name='two'),))}
richset.size_of_group_by(lambda item: item.id % 2)  # => {1: 2, 0: 1}
richset.count_of_group_by(key=lambda item: item.id % 2, predicate=lambda item: item.name.startswith('t'))  # => {1: 1, 0: 1}
richset.aggregate_by(key=lambda r: r.id % 2, fn=lambda a, b: a + b.name, initial='')  # => {1: 'onethree', 0: 'two'}

Paging

richset.page(offset=1, limit=2).to_list()  # => [Something(2, 'two'), Something(3, 'three')]
richset.split_into_pages(2).to_list()  # => [RichSet([Something(1, 'one'), Something(2, 'two')]), RichSet([Something(3, 'three')])]

Development

This repository uses lefthook to run the same checks as CI locally, so problems surface before they reach CI.

# Install dependencies
uv sync

# Install the Git hooks (once; requires lefthook on your PATH)
lefthook install

Once installed, the hooks run automatically:

  • pre-commit: uv run poe check (ruff + mypy)
  • pre-push: uv run poe check and uv run poe test (pytest)

You can also run the checks manually:

uv run poe check  # lint + type check
uv run poe test   # run tests

CI still runs the full matrix (see .github/workflows/); the hooks only bring that feedback earlier on your machine.

LICENSE

The 3-Clause BSD License. See also LICENSE file.

Metadata

Release files for richset 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for richset 0.5.0
File Size Uploaded Provenance Actions
richset-0.5.0.tar.gz 61.2 kB With provenance Details for richset-0.5.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for richset 0.5.0
File Interpreter ABI Platform Actions
richset-0.5.0-py3-none-any.whl Python 3 none any Details for richset-0.5.0-py3-none-any.whl

Total release size: 72.3 kB

Release files / richset-0.5.0.tar.gz

Download URL richset-0.5.0.tar.gz
Size 61.2 kB
Tags Source
SHA-256 checksum
How to use checksums
2aa6ae3d294be8638dd30992cd4418df01012977082aa84b1c1b692281bfddf6
BLAKE2b-256 checksum
How to use checksums
22e0e0f31e3f02c2411ce57a4edfd0ce977649d17aa90c44453dffc93251c3ef
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.

Transparency log

Release files / richset-0.5.0-py3-none-any.whl

Download URL richset-0.5.0-py3-none-any.whl
Size 11.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5dbdce2ea3ffaa43056584d10ba28769261e432ca3b8b87af72fc79638a35c80
BLAKE2b-256 checksum
How to use checksums
c6e73d975774ba7457cdda21bc22bf8f9ecf36643cafd2a108628b84b665f236
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.

Transparency log

Security for richset 0.5.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for richset-0.5.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page