Skip to content

Commit

Permalink
updates
Browse files Browse the repository at this point in the history
- add COUNT, DISTINCT and FIRST mode to MappingCollector
- rename one_to_one => LAST
- rename one_to_many => ALL
- add strictify
- rename unwrap => listify
- rename dictify => simplify
- update and reorganize README.md
- update tests
- bump version to 0.1.0
  • Loading branch information
erivlis committed Jul 29, 2024
1 parent f771a46 commit 8bf6f23
Show file tree
Hide file tree
Showing 8 changed files with 642 additions and 445 deletions.
184 changes: 105 additions & 79 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,57 +58,9 @@ nested defaultdicts, and unwrapping complex objects.

## Usage

### `dictify`
### Transformers

Converts objects to dictionaries using handlers for mappings, iterables, and classes.

```python
from collections import Counter
from dataclasses import dataclass
from datetime import datetime
from typing import Mapping

from mappingtools import dictify

data = {'key1': 'value1', 'key2': ['item1', 'item2']}
dictified_data = dictify(data)
print(dictified_data)
# Output: {'key1': 'value1', 'key2': ['item1', 'item2']}

counter = Counter({'a': 1, 'b': 2})
print(counter)
# Output: Counter({'b': 2, 'a': 1})

dictified_counter = dictify(counter)
print(dictified_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))

dictified_sample_dataclas = dictify(sample_dataclass)
print(dictified_sample_dataclas)
# 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)}
```

### `distinct`
#### `distinct`

Yields distinct values for a specified key across multiple mappings.

Expand All @@ -125,7 +77,7 @@ print(distinct_values)
# Output: [1, 2]
```

### `keep`
#### `keep`

Yields subsets of mappings by retaining only the specified keys.

Expand All @@ -141,7 +93,24 @@ result = list(keep(keys_to_keep, *mappings))
# result: [{'a': 1, 'b': 2}, {'a': 4, 'b': 5}]
```

### `inverse`
#### `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.

```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.

Expand All @@ -154,50 +123,107 @@ print(inverted_mapping)
# Output: defaultdict(<class 'set'>, {1: {'a'}, 2: {'a'}, 3: {'b'}})
```

### `nested_defaultdict`
#### `strictify`

Creates a nested defaultdict with specified depth and factory.
Strictify function applies a strict structural conversion to an object using optional converters for keys and values.

```python
from mappingtools import nested_defaultdict
from mappingtools import strictify

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']})})

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}
```

### `remove`
#### `simplify`

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.
Converts objects to strictly structured dictionaries.

```python
from mappingtools import remove
from collections import Counter
from dataclasses import dataclass
from datetime import datetime
from typing import Mapping

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}]
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)}
```

### `unwrap`
#### `listify`

Transforms complex objects into a list of dictionaries with key and value pairs.

```python
from mappingtools import unwrap
from mappingtools import listify

wrapped_data = {'key1': {'subkey': 'value'}, 'key2': ['item1', 'item2']}
unwrapped_data = unwrap(wrapped_data)
unwrapped_data = listify(wrapped_data)
print(unwrapped_data)
# Output: [{'key': 'key1', 'value': [{'key': 'subkey', 'value': 'value'}]}, {'key': 'key2', 'value': ['item1', 'item2']}]
```

### `CategoryCounter`
### Collectors

#### `nested_defaultdict`

Creates a nested defaultdict with specified depth and factory.

```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.
Expand All @@ -217,16 +243,16 @@ 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`
#### `MappingCollector`

A class designed to collect key-value pairs into an internal mapping,
with two modes of operation: one_to_one and one_to_many.
The mode determines whether each key maps to a single value or multiple values.
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.

```python
from mappingtools import MappingCollector, MappingCollectorMode

collector = MappingCollector(MappingCollectorMode.one_to_many)
collector = MappingCollector(MappingCollectorMode.ALL)
collector.add('a', 1)
collector.add('a', 2)
collector.collect([('b', 3), ('b', 4)])
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "mappingtools"
version = "0.0.5"
version = "0.1.0"
authors = [
{ name = "Eran Rivlis", email = "[email protected]" },
]
Expand Down
Loading

0 comments on commit 8bf6f23

Please sign in to comment.