Coverage for app/utility/dict.py: 100%
33 statements
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-28 09:13 +0000
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-28 09:13 +0000
1"""utils module"""
3import re
6def merge_dicts(*dictionaries: dict) -> dict:
7 """Merge multiple dictionaries, keeping the first occurrence of each key.
8 If a key appears in more than one dictionary, the value from the first dictionary containing the key will be used.
10 Example
11 -------
12 >>> merge_dicts({'label': 'string'}, {'c': 'r', 'label': 'something'})
13 {'label': 'string', 'c': 'r'}"""
15 merged_dictionary = {}
16 for dictionary in dictionaries:
17 for key in dictionary.keys():
18 if key not in merged_dictionary:
19 merged_dictionary[key] = dictionary[key]
21 return merged_dictionary
24def filter_dicts(
25 dicts: list[dict[str, float]],
26 inequations: list[str],
27 fixed: None | dict[str, float] = None,
28) -> list[dict[str, float]]:
29 # noinspection PyUnresolvedReferences
30 """Filter a list of dictionaries given a list of inequations of the form "arg1 < arg2" or "arg1 > arg2"
31 :param dicts: list of dictionaries sharing the same keys
32 :param inequations: list of string (inequations)
33 :param fixed: dictionary containing fixed values
35 Examples
36 --------
37 >>> dicts1 = [{'a': a, 'b': b} for a, b in zip([4, 2, 8, 4], [2, 3, 4, 5])]
38 >>> filter_dicts(dicts1, ['b < a'])
39 [{'a': 4, 'b': 2}, {'a': 8, 'b': 4}]
40 >>> dicts2 = [{'a': a} for a in [4, 2, 8, 4]]
41 >>> filter_dicts(dicts2, ['a < b'], {'b': 4.1})
42 [{'a': 4}, {'a': 2}, {'a': 4}]
43 >>> dicts3 = [{'a': a} for a in [4, 2, 8, 4]]
44 >>> filter_dicts(dicts3, ['b < a'], {'b': 4.1})
45 [{'a': 8}]"""
47 keys = dicts[0].keys()
48 if fixed is None:
49 fixed = {}
51 def get_inequality_condition(
52 value1: float | int,
53 value2: float | int,
54 sign: str,
55 ) -> bool:
56 """Get the inequality boolean depending on the value of a and b and sign
57 :param value1: value 1
58 :param value2: value 2
59 :param sign: string containing '>', '<', '>=', '=>', '<=' or '=<'"""
61 operations = {
62 ">": lambda a, b: a > b,
63 "<": lambda a, b: a < b,
64 ">=": lambda a, b: a >= b,
65 "=>": lambda a, b: a >= b,
66 "<=": lambda a, b: a <= b,
67 "=<": lambda a, b: a <= b,
68 }
70 return operations[sign](value1, value2)
72 for inequation in inequations:
73 match = re.search(r"(<=|>=|=<|=>|<|>)", inequation)
74 if not match:
75 raise ValueError(f"Invalid inequality expression: {inequation}")
76 ineq_sign = match.group(0)
77 arg1, arg2 = [s.strip() for s in inequation.split(ineq_sign)]
79 if arg1 in keys and arg2 in keys:
80 dicts = [p for p in dicts if get_inequality_condition(p[arg1], p[arg2], ineq_sign)]
81 elif arg1 in fixed:
82 dicts = [p for p in dicts if get_inequality_condition(fixed[arg1], p[arg2], ineq_sign)]
83 elif arg2 in fixed:
84 dicts = [p for p in dicts if get_inequality_condition(p[arg1], fixed[arg2], ineq_sign)]
86 return dicts
89def list_to_dict(dicts: list[dict]) -> dict | None:
90 """Convert a list of dictionaries to a dictionary of list. All dictionaries must share the same keys"""
92 new_dict = dict()
93 for key in dicts[0]:
94 new_dict[key] = [d[key] for d in dicts]
95 return new_dict