Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion src/usepy/dict/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,25 @@
from .sort_by_key import sort_by_key
from .sort_by_value import sort_by_value
from .merge_dicts import merge_dicts
from .pick import pick
from .omit import omit
from .get import get
from .deep_merge import deep_merge
from .map_keys import map_keys
from .map_values import map_values
from .invert import invert

__all__ = [
"AdDict",
"sort_by_key",
"sort_by_value",
"merge_dicts",
]
# New functions
"pick",
"omit",
"get",
"deep_merge",
"map_keys",
"map_values",
"invert",
]
41 changes: 41 additions & 0 deletions src/usepy/dict/deep_merge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from typing import Dict, Any
import copy


def deep_merge(*dicts: Dict[str, Any]) -> Dict[str, Any]:
"""
Deeply merges multiple dictionaries.

Nested dictionaries are merged recursively. Other values are overwritten.

Args:
*dicts (Dict[str, Any]): The dictionaries to merge.

Returns:
Dict[str, Any]: A new deeply merged dictionary.

Examples:
>>> deep_merge({'a': {'b': 1}}, {'a': {'c': 2}})
{'a': {'b': 1, 'c': 2}}
>>> deep_merge({'a': 1, 'b': 2}, {'b': 3, 'c': 4})
{'a': 1, 'b': 3, 'c': 4}
>>> deep_merge({'a': [1, 2]}, {'a': [3, 4]})
{'a': [3, 4]}
"""
if not dicts:
return {}

result = copy.deepcopy(dicts[0])

for d in dicts[1:]:
for key, value in d.items():
if (
key in result
and isinstance(result[key], dict)
and isinstance(value, dict)
):
result[key] = deep_merge(result[key], value)
else:
result[key] = copy.deepcopy(value)

return result
40 changes: 40 additions & 0 deletions src/usepy/dict/get.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from typing import TypeVar, Dict, Any, Optional, Union, List

K = TypeVar('K')
V = TypeVar('V')


def get(d: Dict[K, Any], path: Union[str, List[K]], default: Optional[V] = None) -> Optional[V]:
"""
Safely gets a value from a nested dictionary using a path.

Args:
d (Dict[K, Any]): The source dictionary.
path (Union[str, List[K]]): The path to the value. Can be a dot-separated string or a list of keys.
default (Optional[V]): The value to return if path doesn't exist. Defaults to None.

Returns:
Optional[V]: The value at the path, or default if not found.

Examples:
>>> get({'a': {'b': {'c': 1}}}, 'a.b.c')
1
>>> get({'a': {'b': {'c': 1}}}, ['a', 'b', 'c'])
1
>>> get({'a': {'b': 1}}, 'a.x.y')
None
>>> get({'a': {'b': 1}}, 'a.x.y', default=0)
0
"""
if isinstance(path, str):
keys = path.split('.')
else:
keys = path

current = d
for key in keys:
if not isinstance(current, dict) or key not in current:
return default
current = current[key]

return current
25 changes: 25 additions & 0 deletions src/usepy/dict/invert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import TypeVar, Dict, Hashable

K = TypeVar('K', bound=Hashable)
V = TypeVar('V', bound=Hashable)


def invert(d: Dict[K, V]) -> Dict[V, K]:
"""
Creates a new dictionary with keys and values swapped.

If there are duplicate values, the last key wins.

Args:
d (Dict[K, V]): The source dictionary.

Returns:
Dict[V, K]: A new dictionary with keys and values swapped.

Examples:
>>> invert({'a': 1, 'b': 2})
{1: 'a', 2: 'b'}
>>> invert({'a': 1, 'b': 1})
{1: 'b'}
"""
return {v: k for k, v in d.items()}
25 changes: 25 additions & 0 deletions src/usepy/dict/map_keys.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import TypeVar, Dict, Callable

K = TypeVar('K')
V = TypeVar('V')
K2 = TypeVar('K2')


def map_keys(d: Dict[K, V], mapper: Callable[[K], K2]) -> Dict[K2, V]:
"""
Creates a new dictionary with keys transformed by a function.

Args:
d (Dict[K, V]): The source dictionary.
mapper (Callable[[K], K2]): A function that transforms each key.

Returns:
Dict[K2, V]: A new dictionary with transformed keys.

Examples:
>>> map_keys({'a': 1, 'b': 2}, str.upper)
{'A': 1, 'B': 2}
>>> map_keys({1: 'a', 2: 'b'}, lambda x: x * 2)
{2: 'a', 4: 'b'}
"""
return {mapper(k): v for k, v in d.items()}
25 changes: 25 additions & 0 deletions src/usepy/dict/map_values.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import TypeVar, Dict, Callable

K = TypeVar('K')
V = TypeVar('V')
V2 = TypeVar('V2')


def map_values(d: Dict[K, V], mapper: Callable[[V], V2]) -> Dict[K, V2]:
"""
Creates a new dictionary with values transformed by a function.

Args:
d (Dict[K, V]): The source dictionary.
mapper (Callable[[V], V2]): A function that transforms each value.

Returns:
Dict[K, V2]: A new dictionary with transformed values.

Examples:
>>> map_values({'a': 1, 'b': 2}, lambda x: x * 2)
{'a': 2, 'b': 4}
>>> map_values({'a': 'x', 'b': 'y'}, str.upper)
{'a': 'X', 'b': 'Y'}
"""
return {k: mapper(v) for k, v in d.items()}
27 changes: 27 additions & 0 deletions src/usepy/dict/omit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from typing import TypeVar, Dict, Iterable

K = TypeVar('K')
V = TypeVar('V')


def omit(d: Dict[K, V], keys: Iterable[K]) -> Dict[K, V]:
"""
Creates a new dictionary without the specified keys.

Args:
d (Dict[K, V]): The source dictionary.
keys (Iterable[K]): The keys to omit.

Returns:
Dict[K, V]: A new dictionary without the omitted keys.

Examples:
>>> omit({'a': 1, 'b': 2, 'c': 3}, ['b'])
{'a': 1, 'c': 3}
>>> omit({'a': 1, 'b': 2}, ['x', 'y'])
{'a': 1, 'b': 2}
>>> omit({}, ['a'])
{}
"""
keys_set = set(keys)
return {k: v for k, v in d.items() if k not in keys_set}
26 changes: 26 additions & 0 deletions src/usepy/dict/pick.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from typing import TypeVar, Dict, Iterable, Any

K = TypeVar('K')
V = TypeVar('V')


def pick(d: Dict[K, V], keys: Iterable[K]) -> Dict[K, V]:
"""
Creates a new dictionary with only the specified keys.

Args:
d (Dict[K, V]): The source dictionary.
keys (Iterable[K]): The keys to pick.

Returns:
Dict[K, V]: A new dictionary with only the picked keys.

Examples:
>>> pick({'a': 1, 'b': 2, 'c': 3}, ['a', 'c'])
{'a': 1, 'c': 3}
>>> pick({'a': 1, 'b': 2}, ['x', 'y'])
{}
>>> pick({}, ['a'])
{}
"""
return {k: d[k] for k in keys if k in d}
Loading
Loading