X-Git-Url: https://wannabe.guru.org/gitweb/?a=blobdiff_plain;f=src%2Fpyutils%2Fdict_utils.py;h=8e6d6cb9b3c357398104a6b48af61064d4f77217;hb=HEAD;hp=1071bf5d63e959db43ad5fc1123e58012fbb235f;hpb=77513ea630d72318684cf1d0a9198a22f4b547a7;p=pyutils.git diff --git a/src/pyutils/dict_utils.py b/src/pyutils/dict_utils.py index 1071bf5..8e6d6cb 100644 --- a/src/pyutils/dict_utils.py +++ b/src/pyutils/dict_utils.py @@ -5,26 +5,33 @@ """This module contains helper functions for dealing with Python dictionaries.""" from itertools import islice -from typing import Any, Callable, Dict, Iterator, List, Tuple +from typing import Any, Callable, Dict, Hashable, Iterator, List, Tuple from pyutils import dataclass_utils +from pyutils.typez.typing import Comparable + +AnyDict = Dict[Hashable, Any] +DictWithComparableKeys = Dict[Comparable, Any] def init_or_inc( - d: Dict[Any, Any], - key: Any, + d: AnyDict, + key: Hashable, *, init_value: Any = 1, inc_function: Callable[..., Any] = lambda x: x + 1, ) -> bool: - """ - Initialize a dict value (if it doesn't exist) or increments it (using the + """Initialize a dict value (if it doesn't exist) or increments it (using the inc_function, which is customizable) if it already does exist. + See also :py:class:`defaultdict` + (https://docs.python.org/3/library/collections.html#collections.defaultdict) + for a more pythonic alternative. + Args: d: the dict to increment or initialize a value in key: the key to increment or initialize - init_value: default initial value + init_value: default initial value (see also :meth:`dict.setdefault`) inc_function: Callable use to increment a value Returns: @@ -42,6 +49,7 @@ def init_or_inc( False >>> d {'test': 2, 'ing': 1} + """ if key in d.keys(): d[key] = inc_function(d[key]) @@ -50,7 +58,7 @@ def init_or_inc( return False -def shard(d: Dict[Any, Any], size: int) -> Iterator[Dict[Any, Any]]: +def shard(d: AnyDict, size: int) -> Iterator[AnyDict]: """ Shards (i.e. splits) a dict into N subdicts which, together, contain all keys/values from the original unsharded dict. @@ -117,10 +125,10 @@ def raise_on_duplicated_keys(key, new_value, old_value): def coalesce( - inputs: Iterator[Dict[Any, Any]], + inputs: Iterator[AnyDict], *, aggregation_function: Callable[[Any, Any, Any], Any] = coalesce_by_creating_list, -) -> Dict[Any, Any]: +) -> AnyDict: """Coalesce (i.e. combine) N input dicts into one output dict ontaining the union of all keys / values in every input dict. When keys collide, apply the aggregation_function which, by @@ -163,7 +171,7 @@ def coalesce( ... Exception: Key b is duplicated in more than one input dict. """ - out: Dict[Any, Any] = {} + out: AnyDict = {} for d in inputs: for key in d: if key in out: @@ -174,7 +182,7 @@ def coalesce( return out -def item_with_max_value(d: Dict[Any, Any]) -> Tuple[Any, Any]: +def item_with_max_value(d: AnyDict) -> Tuple[Hashable, Any]: """ Args: d: a dict with comparable values @@ -195,7 +203,7 @@ def item_with_max_value(d: Dict[Any, Any]) -> Tuple[Any, Any]: return max(d.items(), key=lambda _: _[1]) -def item_with_min_value(d: Dict[Any, Any]) -> Tuple[Any, Any]: +def item_with_min_value(d: AnyDict) -> Tuple[Hashable, Any]: """ Args: d: a dict with comparable values @@ -212,7 +220,7 @@ def item_with_min_value(d: Dict[Any, Any]) -> Tuple[Any, Any]: return min(d.items(), key=lambda _: _[1]) -def key_with_max_value(d: Dict[Any, Any]) -> Any: +def key_with_max_value(d: AnyDict) -> Hashable: """ Args: d: a dict with comparable keys @@ -232,7 +240,7 @@ def key_with_max_value(d: Dict[Any, Any]) -> Any: return item_with_max_value(d)[0] -def key_with_min_value(d: Dict[Any, Any]) -> Any: +def key_with_min_value(d: AnyDict) -> Hashable: """ Args: d: a dict with comparable keys @@ -252,7 +260,7 @@ def key_with_min_value(d: Dict[Any, Any]) -> Any: return item_with_min_value(d)[0] -def max_value(d: Dict[Any, Any]) -> Any: +def max_value(d: AnyDict) -> Any: """ Args: d: a dict with compatable values @@ -267,7 +275,7 @@ def max_value(d: Dict[Any, Any]) -> Any: return item_with_max_value(d)[1] -def min_value(d: Dict[Any, Any]) -> Any: +def min_value(d: AnyDict) -> Any: """ Args: d: a dict with comparable values @@ -282,7 +290,7 @@ def min_value(d: Dict[Any, Any]) -> Any: return item_with_min_value(d)[1] -def max_key(d: Dict[Any, Any]) -> Any: +def max_key(d: DictWithComparableKeys) -> Comparable: """ Args: d: a dict with comparable keys @@ -300,7 +308,7 @@ def max_key(d: Dict[Any, Any]) -> Any: return max(d.keys()) -def min_key(d: Dict[Any, Any]) -> Any: +def min_key(d: DictWithComparableKeys) -> Comparable: """ Args: d: a dict with comparable keys @@ -318,7 +326,7 @@ def min_key(d: Dict[Any, Any]) -> Any: return min(d.keys()) -def parallel_lists_to_dict(keys: List[Any], values: List[Any]) -> Dict[Any, Any]: +def parallel_lists_to_dict(keys: List[Hashable], values: List[Any]) -> AnyDict: """Given two parallel lists (keys and values), create and return a dict. @@ -329,17 +337,20 @@ def parallel_lists_to_dict(keys: List[Any], values: List[Any]) -> Dict[Any, Any] Returns: A dict composed of zipping the keys list and values list together. + Raises: + ValueError: if keys and values lists not the same length. + >>> k = ['name', 'phone', 'address', 'zip'] >>> v = ['scott', '555-1212', '123 main st.', '12345'] >>> parallel_lists_to_dict(k, v) {'name': 'scott', 'phone': '555-1212', 'address': '123 main st.', 'zip': '12345'} """ if len(keys) != len(values): - raise Exception("Parallel keys and values lists must have the same length") + raise ValueError("Parallel keys and values lists must have the same length") return dict(zip(keys, values)) -def dict_to_key_value_lists(d: Dict[Any, Any]) -> Tuple[List[Any], List[Any]]: +def dict_to_key_value_lists(d: AnyDict) -> Tuple[List[Hashable], List[Any]]: """Given a dict, decompose it into a list of keys and values. Args: