# MappingTools
This library provides utility functions for manipulating and transforming data structures which have or include
Mapping-like characteristics. Including inverting dictionaries, converting class like objects to dictionaries, creating
nested defaultdicts, and unwrapping complex objects.
<table>
<tr style="vertical-align: middle;">
<td>Package</td>
<td>
<img alt="PyPI - version" src="https://img.shields.io/pypi/v/mappingtools">
<img alt="PyPI - Status" src="https://img.shields.io/pypi/status/mappingtools">
<img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/mappingtools">
<img alt="PyPI - Downloads" src="https://img.shields.io/pypi/dd/mappingtools">
<br>
<img alt="GitHub" src="https://img.shields.io/github/license/erivlis/mappingtools">
<img alt="GitHub repo size" src="https://img.shields.io/github/repo-size/erivlis/mappingtools">
<img alt="GitHub last commit (by committer)" src="https://img.shields.io/github/last-commit/erivlis/mappingtools">
<a href="https://github.com/erivlis/mappingtools/graphs/contributors"><img alt="Contributors" src="https://img.shields.io/github/contributors/erivlis/mappingtools.svg"></a>
</td>
</tr>
<tr>
<td>Tools</td>
<td>
<a href="https://www.jetbrains.com/pycharm/"><img alt="PyCharm" src="https://img.shields.io/badge/PyCharm-FCF84A.svg?logo=PyCharm&logoColor=black&labelColor=21D789&color=FCF84A"></a>
<a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff" style="max-width:100%;"></a>
<a href="https://github.com/astral-sh/uv"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json" alt="uv" style="max-width:100%;"></a>
<!-- a href="https://squidfunk.github.io/mkdocs-material/"><img src="https://img.shields.io/badge/Material_for_MkDocs-526CFE?&logo=MaterialForMkDocs&logoColor=white&labelColor=grey"></a -->
</td>
</tr>
<tr>
<td>CI/CD</td>
<td>
<a href="https://github.com/erivlis/mappingtools/actions/workflows/test.yml"><img alt="Tests" src="https://github.com/erivlis/mappingtools/actions/workflows/test.yml/badge.svg?branch=main"></a>
<a href="https://github.com/erivlis/mappingtools/actions/workflows/publish.yml"><img alt="Publish" src="https://github.com/erivlis/mappingtools/actions/workflows/publish.yml/badge.svg"></a>
<!--a href="https://github.com/erivlis/mappingtools/actions/workflows/publish-docs.yaml"><img alt="Publish Docs" src="https://github.com/erivlis/mappingtools/actions/workflows/publish-docs.yaml/badge.svg"></a-->
</td>
</tr>
<tr>
<td>Scans</td>
<td>
<a href="https://codecov.io/gh/erivlis/mappingtools"><img alt="Coverage" src="https://codecov.io/gh/erivlis/mappingtools/graph/badge.svg?token=POODT8M9NV"/></a>
<br>
<a href="https://sonarcloud.io/summary/new_code?id=erivlis_mappingtools"><img alt="Quality Gate Status" src="https://sonarcloud.io/api/project_badges/measure?project=erivlis_mappingtools&metric=alert_status"></a>
<a href="https://sonarcloud.io/summary/new_code?id=erivlis_mappingtools"><img alt="Security Rating" src="https://sonarcloud.io/api/project_badges/measure?project=erivlis_mappingtools&metric=security_rating"></a>
<a href="https://sonarcloud.io/summary/new_code?id=erivlis_mappingtools"><img alt="Maintainability Rating" src="https://sonarcloud.io/api/project_badges/measure?project=erivlis_mappingtools&metric=sqale_rating"></a>
<a href="https://sonarcloud.io/summary/new_code?id=erivlis_mappingtools"><img alt="Reliability Rating" src="https://sonarcloud.io/api/project_badges/measure?project=erivlis_mappingtools&metric=reliability_rating"></a>
<br>
<a href="https://sonarcloud.io/summary/new_code?id=erivlis_mappingtools"><img alt="Lines of Code" src="https://sonarcloud.io/api/project_badges/measure?project=erivlis_mappingtools&metric=ncloc"></a>
<a href="https://sonarcloud.io/summary/new_code?id=erivlis_mappingtools"><img alt="Vulnerabilities" src="https://sonarcloud.io/api/project_badges/measure?project=erivlis_mappingtools&metric=vulnerabilities"></a>
<a href="https://sonarcloud.io/summary/new_code?id=erivlis_mappingtools"><img alt="Bugs" src="https://sonarcloud.io/api/project_badges/measure?project=erivlis_mappingtools&metric=bugs"></a>
<br>
<a href="https://app.codacy.com/gh/erivlis/mappingtools/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade"><img alt="Codacy Quality" src="https://app.codacy.com/project/badge/Grade/8b83a99f939b4883ae2f37d7ec3419d1"></a>
<a href="https://app.codacy.com/gh/erivlis/mappingtools/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage"><img alt="Codacy Coverage" src="https://app.codacy.com/project/badge/Coverage/8b83a99f939b4883ae2f37d7ec3419d1"/></a>
</td>
</tr>
</table>
## Usage
### Transformers
#### `distinct`
Yields distinct values for a specified key across multiple mappings.
<!-- name: test_distinct -->
```python
from mappingtools import distinct
mappings = [
{'a': 1, 'b': 2},
{'a': 2, 'b': 3},
{'a': 1, 'b': 4}
]
distinct_values = list(distinct('a', *mappings))
print(distinct_values)
# Output: [1, 2]
```
#### `keep`
Yields subsets of mappings by retaining only the specified keys.
<!-- name: test_keep -->
```python
from mappingtools import keep
mappings = [
{'a': 1, 'b': 2, 'c': 3},
{'a': 4, 'b': 5, 'd': 6}
]
keys_to_keep = ['a', 'b']
result = list(keep(keys_to_keep, *mappings))
# result: [{'a': 1, 'b': 2}, {'a': 4, 'b': 5}]
```
#### `remove`
Yields mappings with specified keys removed. It takes an iterable of keys and multiple mappings, and returns a generator
of mappings with those keys excluded.
<!-- name: test_remove -->
```python
from mappingtools import remove
mappings = [
{'a': 1, 'b': 2, 'c': 3},
{'a': 4, 'b': 5, 'd': 6}
]
keys_to_remove = ['a', 'b']
result = list(remove(keys_to_remove, *mappings))
# result: [{'c': 3}, {'d': 6}]
```
#### `inverse`
Swaps keys and values in a dictionary.
<!-- name: test_inverse -->
```python
from mappingtools import inverse
original_mapping = {'a': {1, 2}, 'b': {3}}
inverted_mapping = inverse(original_mapping)
print(inverted_mapping)
# Output: defaultdict(<class 'set'>, {1: {'a'}, 2: {'a'}, 3: {'b'}})
```
#### `flattened`
The flattened function takes a nested mapping structure and converts it into a single-level dictionary by flattening the
keys into tuples.
<!-- name: test_flattened -->
```python
from mappingtools import flattened
nested_dict = {
'a': {'b': 1, 'c': {'d': 2}},
'e': 3
}
flat_dict = flattened(nested_dict)
# Expected output: {('a', 'b'): 1, ('a', 'c', 'd'): 2, ('e',): 3}
```
#### `strictify`
Strictify function applies a strict structural conversion to an object using optional converters for keys and values.
<!-- name: test_strictify -->
```python
from mappingtools import strictify
def uppercase_key(key):
return key.upper()
def double_value(value):
return value * 2
data = {'a': 1, 'b': 2}
result = strictify(data, key_converter=uppercase_key, value_converter=double_value)
print(result)
# Output: {'A': 2, 'B': 4}
```
#### `simplify`
Converts objects to strictly structured dictionaries.
<!-- name: test_simplify -->
```python
from collections import Counter
from dataclasses import dataclass
from datetime import datetime
from typing import Mapping
from mappingtools import simplify
data = {'key1': 'value1', 'key2': ['item1', 'item2']}
simplified_data = simplify(data)
print(simplified_data)
# Output: {'key1': 'value1', 'key2': ['item1', 'item2']}
counter = Counter({'a': 1, 'b': 2})
print(counter)
# Output: Counter({'b': 2, 'a': 1})
simplified_counter = simplify(counter)
print(simplified_counter)
# Output: {'a': 1, 'b': 2}
@dataclass
class SampleDataClass:
a: int
b: int
aa: str
bb: str
c: list[int]
d: Mapping
e: datetime
sample_datetime = datetime(2024, 7, 22, 21, 42, 17, 314159)
sample_dataclass = SampleDataClass(1, 2, '11', '22', [1, 2], {'aaa': 111, 'bbb': '222'}, sample_datetime)
print(sample_dataclass)
# Output: SampleDataClass(a=1, b=2, aa='11', bb='22', c=[1, 2], d={'aaa': 111, 'bbb': '222'}, e=datetime.datetime(2024, 7, 22, 21, 42, 17, 314159))
simplified_sample_dataclass = simplify(sample_dataclass)
print(simplified_sample_dataclass)
# Output: {'a': 1, 'aa': '11', 'b': 2, 'bb': '22', 'c': [1, 2], 'd': {'aaa': 111, 'bbb': '222'}, 'e': datetime.datetime(2024, 7, 22, 21, 42, 17, 314159)}
```
#### `listify`
Transforms complex objects into a list of dictionaries with key and value pairs.
<!-- name: test_listify -->
```python
from mappingtools import listify
wrapped_data = {'key1': {'subkey': 'value'}, 'key2': ['item1', 'item2']}
unwrapped_data = listify(wrapped_data)
print(unwrapped_data)
# Output: [{'key': 'key1', 'value': [{'key': 'subkey', 'value': 'value'}]}, {'key': 'key2', 'value': ['item1', 'item2']}]
```
#### `stream`
Takes a mapping and an optional item factory function, and generates items from the mapping.
If the item factory is provided, it applies the factory to each key-value pair before yielding.
<!-- name: test_stream -->
```python
from collections import namedtuple
from mappingtools import stream
def custom_factory(key, value):
return f"{key}: {value}"
my_mapping = {'a': 1, 'b': 2, 'c': 3}
for item in stream(my_mapping, custom_factory):
print(item)
# Output:
# a: 1
# b: 2
# c: 3
MyTuple = namedtuple('MyTuple', ['key', 'value'])
data = {'a': 1, 'b': 2}
for item in stream(data, MyTuple):
print(item)
# Output:
# MyTuple(key='a', value=1)
# MyTuple(key='b', value=2)
def record(k, v):
return {'key': k, 'value': v}
for item in stream(data, record):
print(item)
# output:
# {'key': 'a', 'value': 1}
# {'key': 'b', 'value': 2}
```
#### `stream_dict_records`
generates dictionary records from a given mapping, where each record contains a key-value pair from the mapping with
customizable key and value names.
<!-- name: test_stream_dict_records -->
```python
from mappingtools import stream_dict_records
mapping = {'a': 1, 'b': 2}
records = stream_dict_records(mapping, key_name='letter', value_name='number')
for record in records:
print(record)
# Output:
# {'letter': 'a', 'number': 1}
# {'letter': 'b', 'number': 2}
```
### Collectors
#### `nested_defaultdict`
Creates a nested defaultdict with specified depth and factory.
<!-- name: test_nested_defaultdict -->
```python
from mappingtools import nested_defaultdict
nested_dd = nested_defaultdict(1, list)
nested_dd[0][1].append('value')
print(nested_dd)
# Output: defaultdict(<function nested_defaultdict.<locals>.factory at ...>, {0: defaultdict(<function nested_defaultdict.<locals>.factory at ...>, {1: ['value']})})
```
#### `CategoryCounter`
The CategoryCounter class extends a dictionary to count occurrences of data items categorized by multiple categories.
It maintains a total count of all data items and allows categorization using direct values or functions.
<!-- name: test_category_counter -->
```python
from mappingtools import CategoryCounter
counter = CategoryCounter()
for fruit in ['apple', 'banana', 'apple']:
counter.update({fruit: 1}, type='fruit', char_count=len(fruit), unique_char_count=len(set(fruit)))
print(counter.total)
# Output: Counter({'apple': 2, 'banana': 1})
print(counter)
# Output: CategoryCounter({'type': defaultdict(<class 'collections.Counter'>, {'fruit': Counter({'apple': 2, 'banana': 1})}), 'char_count': defaultdict(<class 'collections.Counter'>, {5: Counter({'apple': 2}), 6: Counter({'banana': 1})}), 'unique_char_count': defaultdict(<class 'collections.Counter'>, {4: Counter({'apple': 2}), 3: Counter({'banana': 1})})})
```
#### `MappingCollector`
A class designed to collect key-value pairs into an internal mapping based on different modes.
It supports modes like ALL, COUNT, DISTINCT, FIRST, and LAST, each dictating how key-value pairs are
collected.
<!-- name: test_mapping_collector -->
```python
from mappingtools import MappingCollector, MappingCollectorMode
collector = MappingCollector(MappingCollectorMode.ALL)
collector.add('a', 1)
collector.add('a', 2)
collector.collect([('b', 3), ('b', 4)])
print(collector.mapping)
# Output: {'a': [1, 2], 'b': [3, 4]}
```
## Development
### Ruff
```shell
ruff check src
```
### Test
#### Standard (cobertura) XML Coverage Report
```shell
python -m pytest tests -n auto --cov=src --cov-branch --doctest-modules --cov-report=xml
```
#### HTML Coverage Report
```shell
python -m pytest tests -n auto --cov=src --cov-branch --doctest-modules --cov-report=html
```
Raw data
{
"_id": null,
"home_page": null,
"name": "mappingtools",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "Mapping, manipulate, mutate, transform",
"author": null,
"author_email": "Eran Rivlis <eran@rivlis.info>",
"download_url": "https://files.pythonhosted.org/packages/9f/b1/fd8e319fad786b3d32609673f9733924bb9ca113b66f7d36217443488060/mappingtools-0.2.2.tar.gz",
"platform": null,
"description": "# MappingTools\n\nThis library provides utility functions for manipulating and transforming data structures which have or include\nMapping-like characteristics. Including inverting dictionaries, converting class like objects to dictionaries, creating\nnested defaultdicts, and unwrapping complex objects.\n\n<table>\n <tr style=\"vertical-align: middle;\">\n <td>Package</td>\n <td>\n <img alt=\"PyPI - version\" src=\"https://img.shields.io/pypi/v/mappingtools\">\n <img alt=\"PyPI - Status\" src=\"https://img.shields.io/pypi/status/mappingtools\">\n <img alt=\"PyPI - Python Version\" src=\"https://img.shields.io/pypi/pyversions/mappingtools\">\n <img alt=\"PyPI - Downloads\" src=\"https://img.shields.io/pypi/dd/mappingtools\">\n <br>\n <img alt=\"GitHub\" src=\"https://img.shields.io/github/license/erivlis/mappingtools\">\n <img alt=\"GitHub repo size\" src=\"https://img.shields.io/github/repo-size/erivlis/mappingtools\">\n <img alt=\"GitHub last commit (by committer)\" src=\"https://img.shields.io/github/last-commit/erivlis/mappingtools\">\n <a href=\"https://github.com/erivlis/mappingtools/graphs/contributors\"><img alt=\"Contributors\" src=\"https://img.shields.io/github/contributors/erivlis/mappingtools.svg\"></a>\n </td>\n </tr>\n <tr>\n <td>Tools</td>\n <td>\n <a href=\"https://www.jetbrains.com/pycharm/\"><img alt=\"PyCharm\" src=\"https://img.shields.io/badge/PyCharm-FCF84A.svg?logo=PyCharm&logoColor=black&labelColor=21D789&color=FCF84A\"></a>\n <a href=\"https://github.com/astral-sh/ruff\"><img src=\"https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json\" alt=\"Ruff\" style=\"max-width:100%;\"></a>\n <a href=\"https://github.com/astral-sh/uv\"><img src=\"https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json\" alt=\"uv\" style=\"max-width:100%;\"></a>\n <!-- a href=\"https://squidfunk.github.io/mkdocs-material/\"><img src=\"https://img.shields.io/badge/Material_for_MkDocs-526CFE?&logo=MaterialForMkDocs&logoColor=white&labelColor=grey\"></a -->\n </td>\n </tr>\n <tr>\n <td>CI/CD</td>\n <td>\n <a href=\"https://github.com/erivlis/mappingtools/actions/workflows/test.yml\"><img alt=\"Tests\" src=\"https://github.com/erivlis/mappingtools/actions/workflows/test.yml/badge.svg?branch=main\"></a>\n <a href=\"https://github.com/erivlis/mappingtools/actions/workflows/publish.yml\"><img alt=\"Publish\" src=\"https://github.com/erivlis/mappingtools/actions/workflows/publish.yml/badge.svg\"></a>\n <!--a href=\"https://github.com/erivlis/mappingtools/actions/workflows/publish-docs.yaml\"><img alt=\"Publish Docs\" src=\"https://github.com/erivlis/mappingtools/actions/workflows/publish-docs.yaml/badge.svg\"></a-->\n </td>\n </tr>\n <tr>\n <td>Scans</td>\n <td>\n <a href=\"https://codecov.io/gh/erivlis/mappingtools\"><img alt=\"Coverage\" src=\"https://codecov.io/gh/erivlis/mappingtools/graph/badge.svg?token=POODT8M9NV\"/></a>\n <br>\n <a href=\"https://sonarcloud.io/summary/new_code?id=erivlis_mappingtools\"><img alt=\"Quality Gate Status\" src=\"https://sonarcloud.io/api/project_badges/measure?project=erivlis_mappingtools&metric=alert_status\"></a>\n <a href=\"https://sonarcloud.io/summary/new_code?id=erivlis_mappingtools\"><img alt=\"Security Rating\" src=\"https://sonarcloud.io/api/project_badges/measure?project=erivlis_mappingtools&metric=security_rating\"></a>\n <a href=\"https://sonarcloud.io/summary/new_code?id=erivlis_mappingtools\"><img alt=\"Maintainability Rating\" src=\"https://sonarcloud.io/api/project_badges/measure?project=erivlis_mappingtools&metric=sqale_rating\"></a>\n <a href=\"https://sonarcloud.io/summary/new_code?id=erivlis_mappingtools\"><img alt=\"Reliability Rating\" src=\"https://sonarcloud.io/api/project_badges/measure?project=erivlis_mappingtools&metric=reliability_rating\"></a>\n <br>\n <a href=\"https://sonarcloud.io/summary/new_code?id=erivlis_mappingtools\"><img alt=\"Lines of Code\" src=\"https://sonarcloud.io/api/project_badges/measure?project=erivlis_mappingtools&metric=ncloc\"></a>\n <a href=\"https://sonarcloud.io/summary/new_code?id=erivlis_mappingtools\"><img alt=\"Vulnerabilities\" src=\"https://sonarcloud.io/api/project_badges/measure?project=erivlis_mappingtools&metric=vulnerabilities\"></a>\n <a href=\"https://sonarcloud.io/summary/new_code?id=erivlis_mappingtools\"><img alt=\"Bugs\" src=\"https://sonarcloud.io/api/project_badges/measure?project=erivlis_mappingtools&metric=bugs\"></a>\n <br>\n <a href=\"https://app.codacy.com/gh/erivlis/mappingtools/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade\"><img alt=\"Codacy Quality\" src=\"https://app.codacy.com/project/badge/Grade/8b83a99f939b4883ae2f37d7ec3419d1\"></a>\n <a href=\"https://app.codacy.com/gh/erivlis/mappingtools/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage\"><img alt=\"Codacy Coverage\" src=\"https://app.codacy.com/project/badge/Coverage/8b83a99f939b4883ae2f37d7ec3419d1\"/></a>\n </td>\n </tr>\n</table>\n\n## Usage\n\n### Transformers\n\n#### `distinct`\n\nYields distinct values for a specified key across multiple mappings.\n\n<!-- name: test_distinct -->\n\n```python\nfrom mappingtools import distinct\n\nmappings = [\n {'a': 1, 'b': 2},\n {'a': 2, 'b': 3},\n {'a': 1, 'b': 4}\n]\ndistinct_values = list(distinct('a', *mappings))\nprint(distinct_values)\n# Output: [1, 2]\n```\n\n#### `keep`\n\nYields subsets of mappings by retaining only the specified keys.\n\n<!-- name: test_keep -->\n\n```python\nfrom mappingtools import keep\n\nmappings = [\n {'a': 1, 'b': 2, 'c': 3},\n {'a': 4, 'b': 5, 'd': 6}\n]\nkeys_to_keep = ['a', 'b']\nresult = list(keep(keys_to_keep, *mappings))\n# result: [{'a': 1, 'b': 2}, {'a': 4, 'b': 5}]\n```\n\n#### `remove`\n\nYields mappings with specified keys removed. It takes an iterable of keys and multiple mappings, and returns a generator\nof mappings with those keys excluded.\n\n<!-- name: test_remove -->\n\n```python\nfrom mappingtools import remove\n\nmappings = [\n {'a': 1, 'b': 2, 'c': 3},\n {'a': 4, 'b': 5, 'd': 6}\n]\nkeys_to_remove = ['a', 'b']\nresult = list(remove(keys_to_remove, *mappings))\n# result: [{'c': 3}, {'d': 6}]\n```\n\n#### `inverse`\n\nSwaps keys and values in a dictionary.\n\n<!-- name: test_inverse -->\n\n```python\nfrom mappingtools import inverse\n\noriginal_mapping = {'a': {1, 2}, 'b': {3}}\ninverted_mapping = inverse(original_mapping)\nprint(inverted_mapping)\n# Output: defaultdict(<class 'set'>, {1: {'a'}, 2: {'a'}, 3: {'b'}})\n```\n\n#### `flattened`\n\nThe flattened function takes a nested mapping structure and converts it into a single-level dictionary by flattening the\nkeys into tuples.\n\n<!-- name: test_flattened -->\n\n```python\nfrom mappingtools import flattened\n\nnested_dict = {\n 'a': {'b': 1, 'c': {'d': 2}},\n 'e': 3\n}\nflat_dict = flattened(nested_dict)\n# Expected output: {('a', 'b'): 1, ('a', 'c', 'd'): 2, ('e',): 3}\n```\n\n#### `strictify`\n\nStrictify function applies a strict structural conversion to an object using optional converters for keys and values.\n\n<!-- name: test_strictify -->\n\n```python\nfrom mappingtools import strictify\n\n\ndef uppercase_key(key):\n return key.upper()\n\n\ndef double_value(value):\n return value * 2\n\n\ndata = {'a': 1, 'b': 2}\nresult = strictify(data, key_converter=uppercase_key, value_converter=double_value)\nprint(result)\n# Output: {'A': 2, 'B': 4}\n```\n\n#### `simplify`\n\nConverts objects to strictly structured dictionaries.\n\n<!-- name: test_simplify -->\n\n```python\nfrom collections import Counter\nfrom dataclasses import dataclass\nfrom datetime import datetime\nfrom typing import Mapping\n\nfrom mappingtools import simplify\n\ndata = {'key1': 'value1', 'key2': ['item1', 'item2']}\nsimplified_data = simplify(data)\nprint(simplified_data)\n# Output: {'key1': 'value1', 'key2': ['item1', 'item2']}\n\ncounter = Counter({'a': 1, 'b': 2})\nprint(counter)\n# Output: Counter({'b': 2, 'a': 1})\n\nsimplified_counter = simplify(counter)\nprint(simplified_counter)\n\n\n# Output: {'a': 1, 'b': 2}\n\n\n@dataclass\nclass SampleDataClass:\n a: int\n b: int\n aa: str\n bb: str\n c: list[int]\n d: Mapping\n e: datetime\n\n\nsample_datetime = datetime(2024, 7, 22, 21, 42, 17, 314159)\nsample_dataclass = SampleDataClass(1, 2, '11', '22', [1, 2], {'aaa': 111, 'bbb': '222'}, sample_datetime)\n\nprint(sample_dataclass)\n# Output: SampleDataClass(a=1, b=2, aa='11', bb='22', c=[1, 2], d={'aaa': 111, 'bbb': '222'}, e=datetime.datetime(2024, 7, 22, 21, 42, 17, 314159))\n\nsimplified_sample_dataclass = simplify(sample_dataclass)\nprint(simplified_sample_dataclass)\n# Output: {'a': 1, 'aa': '11', 'b': 2, 'bb': '22', 'c': [1, 2], 'd': {'aaa': 111, 'bbb': '222'}, 'e': datetime.datetime(2024, 7, 22, 21, 42, 17, 314159)}\n```\n\n#### `listify`\n\nTransforms complex objects into a list of dictionaries with key and value pairs.\n\n<!-- name: test_listify -->\n\n```python\nfrom mappingtools import listify\n\nwrapped_data = {'key1': {'subkey': 'value'}, 'key2': ['item1', 'item2']}\nunwrapped_data = listify(wrapped_data)\nprint(unwrapped_data)\n# Output: [{'key': 'key1', 'value': [{'key': 'subkey', 'value': 'value'}]}, {'key': 'key2', 'value': ['item1', 'item2']}]\n```\n\n#### `stream`\n\nTakes a mapping and an optional item factory function, and generates items from the mapping.\nIf the item factory is provided, it applies the factory to each key-value pair before yielding.\n\n<!-- name: test_stream -->\n\n```python\nfrom collections import namedtuple\n\nfrom mappingtools import stream\n\n\ndef custom_factory(key, value):\n return f\"{key}: {value}\"\n\n\nmy_mapping = {'a': 1, 'b': 2, 'c': 3}\n\nfor item in stream(my_mapping, custom_factory):\n print(item)\n# Output:\n# a: 1\n# b: 2\n# c: 3\n\n\nMyTuple = namedtuple('MyTuple', ['key', 'value'])\ndata = {'a': 1, 'b': 2}\n\nfor item in stream(data, MyTuple):\n print(item)\n\n\n# Output:\n# MyTuple(key='a', value=1)\n# MyTuple(key='b', value=2)\n\n\ndef record(k, v):\n return {'key': k, 'value': v}\n\n\nfor item in stream(data, record):\n print(item)\n# output:\n# {'key': 'a', 'value': 1}\n# {'key': 'b', 'value': 2}\n```\n\n#### `stream_dict_records`\n\ngenerates dictionary records from a given mapping, where each record contains a key-value pair from the mapping with\ncustomizable key and value names.\n\n<!-- name: test_stream_dict_records -->\n\n```python\nfrom mappingtools import stream_dict_records\n\nmapping = {'a': 1, 'b': 2}\nrecords = stream_dict_records(mapping, key_name='letter', value_name='number')\nfor record in records:\n print(record)\n# Output:\n# {'letter': 'a', 'number': 1}\n# {'letter': 'b', 'number': 2}\n```\n\n### Collectors\n\n#### `nested_defaultdict`\n\nCreates a nested defaultdict with specified depth and factory.\n\n<!-- name: test_nested_defaultdict -->\n\n```python\nfrom mappingtools import nested_defaultdict\n\nnested_dd = nested_defaultdict(1, list)\nnested_dd[0][1].append('value')\nprint(nested_dd)\n# Output: defaultdict(<function nested_defaultdict.<locals>.factory at ...>, {0: defaultdict(<function nested_defaultdict.<locals>.factory at ...>, {1: ['value']})})\n```\n\n#### `CategoryCounter`\n\nThe CategoryCounter class extends a dictionary to count occurrences of data items categorized by multiple categories.\nIt maintains a total count of all data items and allows categorization using direct values or functions.\n\n<!-- name: test_category_counter -->\n\n```python\nfrom mappingtools import CategoryCounter\n\ncounter = CategoryCounter()\n\nfor fruit in ['apple', 'banana', 'apple']:\n counter.update({fruit: 1}, type='fruit', char_count=len(fruit), unique_char_count=len(set(fruit)))\n\nprint(counter.total)\n# Output: Counter({'apple': 2, 'banana': 1})\n\nprint(counter)\n# Output: CategoryCounter({'type': defaultdict(<class 'collections.Counter'>, {'fruit': Counter({'apple': 2, 'banana': 1})}), 'char_count': defaultdict(<class 'collections.Counter'>, {5: Counter({'apple': 2}), 6: Counter({'banana': 1})}), 'unique_char_count': defaultdict(<class 'collections.Counter'>, {4: Counter({'apple': 2}), 3: Counter({'banana': 1})})})\n```\n\n#### `MappingCollector`\n\nA class designed to collect key-value pairs into an internal mapping based on different modes.\nIt supports modes like ALL, COUNT, DISTINCT, FIRST, and LAST, each dictating how key-value pairs are\ncollected.\n\n<!-- name: test_mapping_collector -->\n\n```python\nfrom mappingtools import MappingCollector, MappingCollectorMode\n\ncollector = MappingCollector(MappingCollectorMode.ALL)\ncollector.add('a', 1)\ncollector.add('a', 2)\ncollector.collect([('b', 3), ('b', 4)])\nprint(collector.mapping)\n# Output: {'a': [1, 2], 'b': [3, 4]}\n```\n\n## Development\n\n### Ruff\n\n```shell\nruff check src\n```\n\n### Test\n\n#### Standard (cobertura) XML Coverage Report\n\n```shell\npython -m pytest tests -n auto --cov=src --cov-branch --doctest-modules --cov-report=xml\n```\n\n#### HTML Coverage Report\n\n```shell\npython -m pytest tests -n auto --cov=src --cov-branch --doctest-modules --cov-report=html\n```",
"bugtrack_url": null,
"license": null,
"summary": "MappingTools. Do stuff with Mappings",
"version": "0.2.2",
"project_urls": {
"Bug Tracker": "https://github.com/erivlis/mappingtools/issues",
"Homepage": "https://erivlis.github.io/mappingtools",
"Source": "https://github.com/erivlis/mappingtools"
},
"split_keywords": [
"mapping",
" manipulate",
" mutate",
" transform"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "b359139e483562bd8260818566e0b89be5773c095cf5d234f677d4c3f78adc47",
"md5": "65433f400ac9eb194e59a5552d4dd588",
"sha256": "eed4eae5ec41bc8a1cbd63ea56bcc8eb7820c8b778c22eb4c99e01102a880f8b"
},
"downloads": -1,
"filename": "mappingtools-0.2.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "65433f400ac9eb194e59a5552d4dd588",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 9526,
"upload_time": "2024-10-18T21:04:13",
"upload_time_iso_8601": "2024-10-18T21:04:13.685609Z",
"url": "https://files.pythonhosted.org/packages/b3/59/139e483562bd8260818566e0b89be5773c095cf5d234f677d4c3f78adc47/mappingtools-0.2.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "9fb1fd8e319fad786b3d32609673f9733924bb9ca113b66f7d36217443488060",
"md5": "45206c74b52eb4f43d2ad31a7bd3e016",
"sha256": "ecb62596207a5b167ef774fecfeb77c797262527ba888d08878e4e67666cfceb"
},
"downloads": -1,
"filename": "mappingtools-0.2.2.tar.gz",
"has_sig": false,
"md5_digest": "45206c74b52eb4f43d2ad31a7bd3e016",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 26456,
"upload_time": "2024-10-18T21:04:15",
"upload_time_iso_8601": "2024-10-18T21:04:15.344108Z",
"url": "https://files.pythonhosted.org/packages/9f/b1/fd8e319fad786b3d32609673f9733924bb9ca113b66f7d36217443488060/mappingtools-0.2.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-10-18 21:04:15",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "erivlis",
"github_project": "mappingtools",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "mappingtools"
}