pyochain 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyochain might be problematic. Click here for more details.
- pyochain/__init__.py +5 -0
- pyochain/_core/__init__.py +21 -0
- pyochain/_core/_main.py +184 -0
- pyochain/_core/_protocols.py +43 -0
- pyochain/_dict/__init__.py +4 -0
- pyochain/_dict/_exprs.py +115 -0
- pyochain/_dict/_filters.py +273 -0
- pyochain/_dict/_funcs.py +62 -0
- pyochain/_dict/_groups.py +176 -0
- pyochain/_dict/_iter.py +92 -0
- pyochain/_dict/_joins.py +137 -0
- pyochain/_dict/_main.py +307 -0
- pyochain/_dict/_nested.py +218 -0
- pyochain/_dict/_process.py +171 -0
- pyochain/_iter/__init__.py +3 -0
- pyochain/_iter/_aggregations.py +323 -0
- pyochain/_iter/_booleans.py +224 -0
- pyochain/_iter/_constructors.py +155 -0
- pyochain/_iter/_eager.py +195 -0
- pyochain/_iter/_filters.py +503 -0
- pyochain/_iter/_groups.py +264 -0
- pyochain/_iter/_joins.py +407 -0
- pyochain/_iter/_lists.py +306 -0
- pyochain/_iter/_main.py +224 -0
- pyochain/_iter/_maps.py +358 -0
- pyochain/_iter/_partitions.py +148 -0
- pyochain/_iter/_process.py +384 -0
- pyochain/_iter/_rolling.py +247 -0
- pyochain/_iter/_tuples.py +221 -0
- pyochain/py.typed +0 -0
- pyochain-0.5.0.dist-info/METADATA +295 -0
- pyochain-0.5.0.dist-info/RECORD +33 -0
- pyochain-0.5.0.dist-info/WHEEL +4 -0
pyochain/_dict/_funcs.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def dict_repr(
|
|
5
|
+
v: object,
|
|
6
|
+
depth: int = 0,
|
|
7
|
+
max_depth: int = 3,
|
|
8
|
+
max_items: int = 6,
|
|
9
|
+
max_str: int = 80,
|
|
10
|
+
indent: int = 2,
|
|
11
|
+
) -> str:
|
|
12
|
+
pad = " " * (depth * indent)
|
|
13
|
+
if depth > max_depth:
|
|
14
|
+
return "…"
|
|
15
|
+
match v:
|
|
16
|
+
case dict():
|
|
17
|
+
items: list[tuple[str, Any]] = list(v.items()) # type: ignore
|
|
18
|
+
shown: list[tuple[str, Any]] = items[:max_items]
|
|
19
|
+
if (
|
|
20
|
+
all(
|
|
21
|
+
not isinstance(val, dict) and not isinstance(val, list)
|
|
22
|
+
for _, val in shown
|
|
23
|
+
)
|
|
24
|
+
and len(shown) <= 2
|
|
25
|
+
):
|
|
26
|
+
body = ", ".join(
|
|
27
|
+
f"{k!r}: {dict_repr(val, depth + 1)}" for k, val in shown
|
|
28
|
+
)
|
|
29
|
+
if len(items) > max_items:
|
|
30
|
+
body += ", …"
|
|
31
|
+
return "{" + body + "}"
|
|
32
|
+
lines: list[str] = []
|
|
33
|
+
for k, val in shown:
|
|
34
|
+
lines.append(
|
|
35
|
+
f"{pad}{' ' * indent}{k!r}: {dict_repr(val, depth + 1, max_depth, max_items, max_str, indent)}"
|
|
36
|
+
)
|
|
37
|
+
if len(items) > max_items:
|
|
38
|
+
lines.append(f"{pad}{' ' * indent}…")
|
|
39
|
+
return "{\n" + ",\n".join(lines) + f"\n{pad}" + "}"
|
|
40
|
+
|
|
41
|
+
case list():
|
|
42
|
+
elems: list[Any] = v[:max_items] # type: ignore
|
|
43
|
+
if (
|
|
44
|
+
all(isinstance(x, (int, float, str, bool, type(None))) for x in elems)
|
|
45
|
+
and len(elems) <= 4
|
|
46
|
+
):
|
|
47
|
+
body = ", ".join(dict_repr(x, depth + 1) for x in elems)
|
|
48
|
+
if len(v) > max_items: # type: ignore
|
|
49
|
+
body += ", …"
|
|
50
|
+
return "[" + body + "]"
|
|
51
|
+
lines = [
|
|
52
|
+
f"{pad}{' ' * indent}{dict_repr(x, depth + 1, max_depth, max_items, max_str, indent)}"
|
|
53
|
+
for x in elems
|
|
54
|
+
]
|
|
55
|
+
if len(v) > max_items: # type: ignore
|
|
56
|
+
lines.append(f"{pad}{' ' * indent}…")
|
|
57
|
+
return "[\n" + ",\n".join(lines) + f"\n{pad}" + "]"
|
|
58
|
+
|
|
59
|
+
case str():
|
|
60
|
+
return repr(v if len(v) <= max_str else v[:max_str] + "…")
|
|
61
|
+
case _:
|
|
62
|
+
return repr(v)
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
import cytoolz as cz
|
|
7
|
+
|
|
8
|
+
from .._core import MappingWrapper
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from ._main import Dict
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GroupsDict[K, V](MappingWrapper[K, V]):
|
|
15
|
+
def group_by_value[G](self, func: Callable[[V], G]) -> Dict[G, dict[K, V]]:
|
|
16
|
+
"""
|
|
17
|
+
Group dict items into sub-dictionaries based on a function of the value.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
func: Function to determine the group for each value.
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
>>> import pyochain as pc
|
|
24
|
+
>>> d = {"a": 1, "b": 2, "c": 3, "d": 2}
|
|
25
|
+
>>> pc.Dict(d).group_by_value(lambda v: v % 2).unwrap()
|
|
26
|
+
{1: {'a': 1, 'c': 3}, 0: {'b': 2, 'd': 2}}
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def _group_by_value(data: dict[K, V]) -> dict[G, dict[K, V]]:
|
|
32
|
+
def _(kv: tuple[K, V]) -> G:
|
|
33
|
+
return func(kv[1])
|
|
34
|
+
|
|
35
|
+
return cz.dicttoolz.valmap(dict, cz.itertoolz.groupby(_, data.items()))
|
|
36
|
+
|
|
37
|
+
return self.apply(_group_by_value)
|
|
38
|
+
|
|
39
|
+
def group_by_key[G](self, func: Callable[[K], G]) -> Dict[G, dict[K, V]]:
|
|
40
|
+
"""
|
|
41
|
+
Group dict items into sub-dictionaries based on a function of the key.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
func: Function to determine the group for each key.
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
>>> import pyochain as pc
|
|
48
|
+
>>> d = {"user_1": 10, "user_2": 20, "admin_1": 100}
|
|
49
|
+
>>> pc.Dict(d).group_by_key(lambda k: k.split("_")[0]).unwrap()
|
|
50
|
+
{'user': {'user_1': 10, 'user_2': 20}, 'admin': {'admin_1': 100}}
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def _group_by_key(data: dict[K, V]) -> dict[G, dict[K, V]]:
|
|
56
|
+
def _(kv: tuple[K, V]) -> G:
|
|
57
|
+
return func(kv[0])
|
|
58
|
+
|
|
59
|
+
return cz.dicttoolz.valmap(dict, cz.itertoolz.groupby(_, data.items()))
|
|
60
|
+
|
|
61
|
+
return self.apply(_group_by_key)
|
|
62
|
+
|
|
63
|
+
def group_by_key_agg[G, R](
|
|
64
|
+
self,
|
|
65
|
+
key_func: Callable[[K], G],
|
|
66
|
+
agg_func: Callable[[Dict[K, V]], R],
|
|
67
|
+
) -> Dict[G, R]:
|
|
68
|
+
"""
|
|
69
|
+
Group by key function, then apply aggregation function to each sub-dict.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
key_func: Function to determine the group for each key.
|
|
73
|
+
agg_func: Function to aggregate each sub-dictionary.
|
|
74
|
+
|
|
75
|
+
This avoids materializing intermediate `Dict` objects if you only need
|
|
76
|
+
an aggregated result for each group.
|
|
77
|
+
```python
|
|
78
|
+
>>> import pyochain as pc
|
|
79
|
+
>>>
|
|
80
|
+
>>> data = {"user_1": 10, "user_2": 20, "admin_1": 100}
|
|
81
|
+
>>> pc.Dict(data).group_by_key_agg(
|
|
82
|
+
... key_func=lambda k: k.split("_")[0],
|
|
83
|
+
... agg_func=lambda d: d.iter_values().sum(),
|
|
84
|
+
... ).unwrap()
|
|
85
|
+
{'user': 30, 'admin': 100}
|
|
86
|
+
>>>
|
|
87
|
+
>>> data_files = {
|
|
88
|
+
... "file_a.txt": 100,
|
|
89
|
+
... "file_b.log": 20,
|
|
90
|
+
... "file_c.txt": 50,
|
|
91
|
+
... "file_d.log": 5,
|
|
92
|
+
... }
|
|
93
|
+
>>>
|
|
94
|
+
>>> def get_stats(sub_dict: pc.Dict[str, int]) -> dict[str, Any]:
|
|
95
|
+
... return {
|
|
96
|
+
... "count": sub_dict.iter_keys().count(),
|
|
97
|
+
... "total_size": sub_dict.iter_values().sum(),
|
|
98
|
+
... "max_size": sub_dict.iter_values().max(),
|
|
99
|
+
... "files": sub_dict.iter_keys().sort().into(list),
|
|
100
|
+
... }
|
|
101
|
+
>>>
|
|
102
|
+
>>> pc.Dict(data_files).group_by_key_agg(
|
|
103
|
+
... key_func=lambda k: k.split(".")[-1], agg_func=get_stats
|
|
104
|
+
... ).sort().unwrap()
|
|
105
|
+
{'log': {'count': 2, 'total_size': 25, 'max_size': 20, 'files': ['file_b.log', 'file_d.log']}, 'txt': {'count': 2, 'total_size': 150, 'max_size': 100, 'files': ['file_a.txt', 'file_c.txt']}}
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
"""
|
|
109
|
+
from ._main import Dict
|
|
110
|
+
|
|
111
|
+
def _group_by_key_agg(data: dict[K, V]) -> dict[G, R]:
|
|
112
|
+
def _key_func(kv: tuple[K, V]) -> G:
|
|
113
|
+
return key_func(kv[0])
|
|
114
|
+
|
|
115
|
+
def _agg_func(items: list[tuple[K, V]]) -> R:
|
|
116
|
+
return agg_func(Dict(dict(items)))
|
|
117
|
+
|
|
118
|
+
groups = cz.itertoolz.groupby(_key_func, data.items())
|
|
119
|
+
return cz.dicttoolz.valmap(_agg_func, groups)
|
|
120
|
+
|
|
121
|
+
return self.apply(_group_by_key_agg)
|
|
122
|
+
|
|
123
|
+
def group_by_value_agg[G, R](
|
|
124
|
+
self,
|
|
125
|
+
value_func: Callable[[V], G],
|
|
126
|
+
agg_func: Callable[[Dict[K, V]], R],
|
|
127
|
+
) -> Dict[G, R]:
|
|
128
|
+
"""
|
|
129
|
+
Group by value function, then apply aggregation function to each sub-dict.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
value_func: Function to determine the group for each value.
|
|
133
|
+
agg_func: Function to aggregate each sub-dictionary.
|
|
134
|
+
|
|
135
|
+
This avoids materializing intermediate `Dict` objects if you only need
|
|
136
|
+
an aggregated result for each group.
|
|
137
|
+
```python
|
|
138
|
+
>>> import pyochain as pc
|
|
139
|
+
>>>
|
|
140
|
+
>>> data = {"math": "A", "physics": "B", "english": "A"}
|
|
141
|
+
>>> pc.Dict(data).group_by_value_agg(
|
|
142
|
+
... value_func=lambda grade: grade,
|
|
143
|
+
... agg_func=lambda d: d.iter_keys().count(),
|
|
144
|
+
... ).unwrap()
|
|
145
|
+
{'A': 2, 'B': 1}
|
|
146
|
+
>>>
|
|
147
|
+
>>> # --- Exemple 2: Agrégation plus complexe ---
|
|
148
|
+
>>> sales_data = {
|
|
149
|
+
... "store_1": "Electronics",
|
|
150
|
+
... "store_2": "Groceries",
|
|
151
|
+
... "store_3": "Electronics",
|
|
152
|
+
... "store_4": "Clothing",
|
|
153
|
+
... }
|
|
154
|
+
>>>
|
|
155
|
+
>>> # Obtain the first store for each category (after sorting store names)
|
|
156
|
+
>>> pc.Dict(sales_data).group_by_value_agg(
|
|
157
|
+
... value_func=lambda category: category,
|
|
158
|
+
... agg_func=lambda d: d.iter_keys().sort().first(),
|
|
159
|
+
... ).sort().unwrap()
|
|
160
|
+
{'Clothing': 'store_4', 'Electronics': 'store_1', 'Groceries': 'store_2'}
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
"""
|
|
164
|
+
from ._main import Dict
|
|
165
|
+
|
|
166
|
+
def _group_by_value_agg(data: dict[K, V]) -> dict[G, R]:
|
|
167
|
+
def _key_func(kv: tuple[K, V]) -> G:
|
|
168
|
+
return value_func(kv[1])
|
|
169
|
+
|
|
170
|
+
def _agg_func(items: list[tuple[K, V]]) -> R:
|
|
171
|
+
return agg_func(Dict(dict(items)))
|
|
172
|
+
|
|
173
|
+
groups = cz.itertoolz.groupby(_key_func, data.items())
|
|
174
|
+
return cz.dicttoolz.valmap(_agg_func, groups)
|
|
175
|
+
|
|
176
|
+
return self.apply(_group_by_value_agg)
|
pyochain/_dict/_iter.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable, Iterable, Mapping
|
|
4
|
+
from typing import TYPE_CHECKING, Concatenate
|
|
5
|
+
|
|
6
|
+
import cytoolz as cz
|
|
7
|
+
|
|
8
|
+
from .._core import MappingWrapper
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from .._iter import Iter
|
|
12
|
+
from ._main import Dict
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class IterDict[K, V](MappingWrapper[K, V]):
|
|
16
|
+
def itr[**P, R, U](
|
|
17
|
+
self: MappingWrapper[K, Iterable[U]],
|
|
18
|
+
func: Callable[Concatenate[Iter[U], P], R],
|
|
19
|
+
*args: P.args,
|
|
20
|
+
**kwargs: P.kwargs,
|
|
21
|
+
) -> Dict[K, R]:
|
|
22
|
+
"""
|
|
23
|
+
Apply a function to each value after wrapping it in an Iter.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
func: Function to apply to each value after wrapping it in an Iter.
|
|
27
|
+
*args: Positional arguments to pass to the function.
|
|
28
|
+
**kwargs: Keyword arguments to pass to the function.
|
|
29
|
+
|
|
30
|
+
Syntactic sugar for `map_values(lambda data: func(Iter(data), *args, **kwargs))`
|
|
31
|
+
```python
|
|
32
|
+
>>> import pyochain as pc
|
|
33
|
+
>>> data = {
|
|
34
|
+
... "numbers1": [1, 2, 3],
|
|
35
|
+
... "numbers2": [4, 5, 6],
|
|
36
|
+
... }
|
|
37
|
+
>>> pc.Dict(data).itr(lambda v: v.repeat(5).flatten().sum()).unwrap()
|
|
38
|
+
{'numbers1': 30, 'numbers2': 75}
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
"""
|
|
42
|
+
from .._iter import Iter
|
|
43
|
+
|
|
44
|
+
def _itr(data: Mapping[K, Iterable[U]]) -> dict[K, R]:
|
|
45
|
+
def _(v: Iterable[U]) -> R:
|
|
46
|
+
return func(Iter.from_(v), *args, **kwargs)
|
|
47
|
+
|
|
48
|
+
return cz.dicttoolz.valmap(_, data)
|
|
49
|
+
|
|
50
|
+
return self.apply(_itr)
|
|
51
|
+
|
|
52
|
+
def iter_keys(self) -> Iter[K]:
|
|
53
|
+
"""
|
|
54
|
+
Return a Iter of the dict's keys.
|
|
55
|
+
```python
|
|
56
|
+
>>> import pyochain as pc
|
|
57
|
+
>>> pc.Dict({1: 2}).iter_keys().into(list)
|
|
58
|
+
[1]
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
"""
|
|
62
|
+
from .._iter import Iter
|
|
63
|
+
|
|
64
|
+
return Iter.from_(self.unwrap().keys())
|
|
65
|
+
|
|
66
|
+
def iter_values(self) -> Iter[V]:
|
|
67
|
+
"""
|
|
68
|
+
Return an Iter of the dict's values.
|
|
69
|
+
```python
|
|
70
|
+
>>> import pyochain as pc
|
|
71
|
+
>>> pc.Dict({1: 2}).iter_values().into(list)
|
|
72
|
+
[2]
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
"""
|
|
76
|
+
from .._iter import Iter
|
|
77
|
+
|
|
78
|
+
return Iter.from_(self.unwrap().values())
|
|
79
|
+
|
|
80
|
+
def iter_items(self) -> Iter[tuple[K, V]]:
|
|
81
|
+
"""
|
|
82
|
+
Return a Iter of the dict's items.
|
|
83
|
+
```python
|
|
84
|
+
>>> import pyochain as pc
|
|
85
|
+
>>> pc.Dict({1: 2}).iter_items().into(list)
|
|
86
|
+
[(1, 2)]
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
"""
|
|
90
|
+
from .._iter import Iter
|
|
91
|
+
|
|
92
|
+
return Iter.from_(self.unwrap().items())
|
pyochain/_dict/_joins.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable, Iterable, Mapping
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
import cytoolz as cz
|
|
7
|
+
|
|
8
|
+
from .._core import MappingWrapper
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from ._main import Dict
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class JoinsDict[K, V](MappingWrapper[K, V]):
|
|
15
|
+
def inner_join[W](self, other: Mapping[K, W]) -> Dict[K, tuple[V, W]]:
|
|
16
|
+
"""
|
|
17
|
+
Performs an inner join with another mapping based on keys.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
other: The mapping to join with.
|
|
21
|
+
|
|
22
|
+
Only keys present in both mappings are kept.
|
|
23
|
+
```python
|
|
24
|
+
>>> import pyochain as pc
|
|
25
|
+
>>> d1 = {"a": 1, "b": 2}
|
|
26
|
+
>>> d2 = {"b": 10, "c": 20}
|
|
27
|
+
>>> pc.Dict(d1).inner_join(d2).unwrap()
|
|
28
|
+
{'b': (2, 10)}
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def _inner_join(data: Mapping[K, V]) -> dict[K, tuple[V, W]]:
|
|
34
|
+
return {k: (v, other[k]) for k, v in data.items() if k in other}
|
|
35
|
+
|
|
36
|
+
return self.apply(_inner_join)
|
|
37
|
+
|
|
38
|
+
def left_join[W](self, other: Mapping[K, W]) -> Dict[K, tuple[V, W | None]]:
|
|
39
|
+
"""
|
|
40
|
+
Performs a left join with another mapping based on keys.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
other: The mapping to join with.
|
|
44
|
+
|
|
45
|
+
All keys from the left dictionary (self) are kept.
|
|
46
|
+
```python
|
|
47
|
+
>>> import pyochain as pc
|
|
48
|
+
>>> d1 = {"a": 1, "b": 2}
|
|
49
|
+
>>> d2 = {"b": 10, "c": 20}
|
|
50
|
+
>>> pc.Dict(d1).left_join(d2).unwrap()
|
|
51
|
+
{'a': (1, None), 'b': (2, 10)}
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def _left_join(data: Mapping[K, V]) -> dict[K, tuple[V, W | None]]:
|
|
57
|
+
return {k: (v, other.get(k)) for k, v in data.items()}
|
|
58
|
+
|
|
59
|
+
return self.apply(_left_join)
|
|
60
|
+
|
|
61
|
+
def diff(self, other: Mapping[K, V]) -> Dict[K, tuple[V | None, V | None]]:
|
|
62
|
+
"""
|
|
63
|
+
Returns a dict of the differences between this dict and another.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
other: The mapping to compare against.
|
|
67
|
+
|
|
68
|
+
The keys of the returned dict are the keys that are not shared or have different values.
|
|
69
|
+
The values are tuples containing the value from self and the value from other.
|
|
70
|
+
```python
|
|
71
|
+
>>> import pyochain as pc
|
|
72
|
+
>>> d1 = {"a": 1, "b": 2, "c": 3}
|
|
73
|
+
>>> d2 = {"b": 2, "c": 4, "d": 5}
|
|
74
|
+
>>> pc.Dict(d1).diff(d2).sort().unwrap()
|
|
75
|
+
{'a': (1, None), 'c': (3, 4), 'd': (None, 5)}
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def _diff(
|
|
81
|
+
data: Mapping[K, V], other: Mapping[K, V]
|
|
82
|
+
) -> dict[K, tuple[V | None, V | None]]:
|
|
83
|
+
all_keys: set[K] = data.keys() | other.keys()
|
|
84
|
+
diffs: dict[K, tuple[V | None, V | None]] = {}
|
|
85
|
+
for key in all_keys:
|
|
86
|
+
self_val = data.get(key)
|
|
87
|
+
other_val = other.get(key)
|
|
88
|
+
if self_val != other_val:
|
|
89
|
+
diffs[key] = (self_val, other_val)
|
|
90
|
+
return diffs
|
|
91
|
+
|
|
92
|
+
return self.apply(_diff, other)
|
|
93
|
+
|
|
94
|
+
def merge(self, *others: Mapping[K, V]) -> Dict[K, V]:
|
|
95
|
+
"""
|
|
96
|
+
Merge other dicts into this one and return a new Dict.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
*others: One or more mappings to merge into the current dictionary.
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
>>> import pyochain as pc
|
|
103
|
+
>>> pc.Dict({1: "one"}).merge({2: "two"}).unwrap()
|
|
104
|
+
{1: 'one', 2: 'two'}
|
|
105
|
+
>>> # Later dictionaries have precedence
|
|
106
|
+
>>> pc.Dict({1: 2, 3: 4}).merge({3: 3, 4: 4}).unwrap()
|
|
107
|
+
{1: 2, 3: 3, 4: 4}
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
"""
|
|
111
|
+
return self.apply(cz.dicttoolz.merge, *others)
|
|
112
|
+
|
|
113
|
+
def merge_with(
|
|
114
|
+
self, *others: Mapping[K, V], func: Callable[[Iterable[V]], V]
|
|
115
|
+
) -> Dict[K, V]:
|
|
116
|
+
"""
|
|
117
|
+
Merge dicts using a function to combine values for duplicate keys.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
*others: One or more mappings to merge into the current dictionary.
|
|
121
|
+
func: Function to combine values for duplicate keys.
|
|
122
|
+
|
|
123
|
+
A key may occur in more than one dict, and all values mapped from the key will be passed to the function as a list, such as func([val1, val2, ...]).
|
|
124
|
+
```python
|
|
125
|
+
>>> import pyochain as pc
|
|
126
|
+
>>> pc.Dict({1: 1, 2: 2}).merge_with({1: 10, 2: 20}, func=sum).unwrap()
|
|
127
|
+
{1: 11, 2: 22}
|
|
128
|
+
>>> pc.Dict({1: 1, 2: 2}).merge_with({2: 20, 3: 30}, func=max).unwrap()
|
|
129
|
+
{1: 1, 2: 20, 3: 30}
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
def _merge_with(data: Mapping[K, V]) -> dict[K, V]:
|
|
135
|
+
return cz.dicttoolz.merge_with(func, data, *others)
|
|
136
|
+
|
|
137
|
+
return self.apply(_merge_with)
|