Metadata-Version: 2.4
Name: dictionary-mapper
Version: 0.2.0
Summary: A tool for mapping and transforming dictionaries based on predefined rules.
Project-URL: Documentation, https://github.com/jjalvarezl-python/dictionary-mapper#readme
Project-URL: Issues, https://github.com/jjalvarezl-python/dictionary-mapper/issues
Project-URL: Source, https://github.com/jjalvarezl-python/dictionary-mapper
Author-email: Jhon Alvarez <jjalvarezl@unicauca.edu.co>
License-Expression: MIT
License-File: LICENSE.txt
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=3.8
Requires-Dist: anyio>=4.11.0
Requires-Dist: certifi>=2025.10.5
Requires-Dist: cffi>=2.0.0
Requires-Dist: click>=8.3.0
Requires-Dist: cryptography>=46.0.3
Requires-Dist: filelock>=3.20.0
Requires-Dist: httpcore>=1.0.9
Requires-Dist: httpx>=0.28.1
Requires-Dist: idna>=3.11
Requires-Dist: sniffio>=1.3.1
Requires-Dist: zstandard>=0.25.0
Provides-Extra: dev
Requires-Dist: commitizen>=3.27.0; extra == 'dev'
Requires-Dist: coverage>=7.3.3; extra == 'dev'
Requires-Dist: hatch>=1.15.1; extra == 'dev'
Requires-Dist: hatchling>=1.27.0; extra == 'dev'
Requires-Dist: mypy>=1.18.2; extra == 'dev'
Requires-Dist: pre-commit>=4.4.0; extra == 'dev'
Requires-Dist: pytest>=9.0.0; extra == 'dev'
Requires-Dist: rich>=14.2.0; extra == 'dev'
Requires-Dist: ruff>=0.14.4; extra == 'dev'
Requires-Dist: typing-extensions>=4.15.0; extra == 'dev'
Description-Content-Type: text/markdown

# Dictionary Mapper

[![PyPI - Version](https://img.shields.io/pypi/v/dictionary-mapper.svg)](https://pypi.org/project/dictionary-mapper)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/dictionary-mapper.svg)](https://pypi.org/project/dictionary-mapper)

-----

## Table of Contents

- [Usage](#Usage)
- [Installation](#installation)
- [License](#license)

## Usage

This library can map a source dictionary and generate a target new one following dot notation in source and target dictionaries.

Example:

1. Define a SpecEntry dict, keys are the source dict paths, values are the target dict paths:

```python
from dictionary_mapper import SpecEntry


spec: SpecEntry = {
    "body.int_field": "int_field",
    "body.str_field": "str_field",
    "body.complex_field.nested_int": {
        "path": "complex_field.nested_int",
        "default": 0,
        "transform": lambda x: cast("int", x) * 2,
    },
    "body.complex_field.nested_str": "complex_field.nested_str",
    "body.list[0].str_field": "list_field",
    "body.int_list": "int_list",
    "body.str_list": "str_list",
}
```

2. For raw dict output, then use the RawDictionaryMapper class

```python
...
from typing import cast
from dictionary_mapper import RawDictionaryMapper
...


src: dict[str, object] = {
    "body": {
        "int_field": 10,
        "str_field": "hello",
        "complex_field": {
            "nested_int": 5,
            "nested_str": "world",
        },
        "list": [
            {
                "str_field": "test field",
            },
        ],
        "int_list": [1, 2, 3],
        "str_list": ["1", "2", "3"],
    },
}


dm: RawDictionaryMapper = RawDictionaryMapper()

maped_dict: dict[str, object] = dm.create_transformed_dict(src, spec)

assert maped_dict["int_field"] == 10
assert maped_dict["str_field"] == "hello"
assert cast("dict[str, object]", maped_dict["complex_field"])["nested_int"] == 10  # Transformed
assert cast("dict[str, object]", maped_dict["complex_field"])["nested_str"] == "world"
assert maped_dict["list_field"] == "test field"
assert maped_dict["int_list"] == [1, 2, 3]
assert maped_dict["str_list"] == ["1", "2", "3"]
```

3. For TypedDicts you can use the TypedDictionaryMapper as follows

```python
...
from dictionary_mapper import TypedDictionaryMapper
...


class MyNestedDict(TypedDict):
    nested_int: int
    nested_str: str


class MyTypedDict(TypedDict):
    int_field: int
    str_field: str
    complex_field: MyNestedDict
    list_field: str
    int_list: list[int]
    str_list: list[str]

...

dm: TypedDictionaryMapper[MyTypedDict] = TypedDictionaryMapper()

maped_dict: MyTypedDict = dm.create_transformed_dict(src, spec)

assert maped_dict["int_field"] == 10
assert maped_dict["str_field"] == "hello"
assert maped_dict["complex_field"]["nested_int"] == 10  # Transformed
assert maped_dict["complex_field"]["nested_str"] == "world"
assert maped_dict["list_field"] == "test field"
assert maped_dict["int_list"] == [1, 2, 3]
assert maped_dict["str_list"] == ["1", "2", "3"]
```

4. You can add complex lists on the target, but you'll need to add allways the index to that lists, otherwhise will be recognized as primitive value.

```python
src = {
    ...
    "complex_list": [
        {
            "nested_int": 10,
            "nested_str": "complex",
        },
        {"nested_int": 5},
        {},
        {
            "nested_str": "double complex",
        },
    ],
    ...
}

...

spec = {
    ...
    "body.complex_list[0].nested_int": "complex_list[1].secondary_field[0].secondary_int",
    "body.complex_list[0].nested_str": "complex_list[1].secondary_field[3].secondary_str",
    "body.complex_list[2].nested_int": "complex_list[1].secondary_field[1].secondary_int",
    "body.complex_list[3].nested_str": "complex_list[1].secondary_field[2].secondary_str",
    "body.complex_list[1].nested_int": {
        "path": "complex_list[0].secondary_field[1].secondary_int",
        "default": 0,
        "transform": lambda x: cast("int", x) * 2,
    },
    ...
}

...

EXPECTED_INT_FIELD = 10

assert maped_dict["complex_list"][1]["secondary_field"][0]["secondary_int"] == EXPECTED_INT_FIELD
assert maped_dict["complex_list"][1]["secondary_field"][3]["secondary_str"] == "complex"
assert maped_dict["complex_list"][1]["secondary_field"][1]["secondary_int"] is None
assert maped_dict["complex_list"][1]["secondary_field"][2]["secondary_str"] == "double complex"
assert maped_dict["complex_list"][0]["secondary_field"][1]["secondary_int"] == EXPECTED_INT_FIELD  # Transformed

```

## Installation

Add recommended extensions at `.vscode/extensions.json`. Then run:

```console
pip install hatch hatch-pip-deepfreeze
hatch shell
```
These two commands creates everithing for you and vscode to start working ASAP.

## License

`dictionary-mapper` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
