forked from cool-RR/python_toolbox
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdict_tools.py
More file actions
198 lines (153 loc) · 6.62 KB
/
dict_tools.py
File metadata and controls
198 lines (153 loc) · 6.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# Copyright 2009-2015 Ram Rachum.
# This program is distributed under the MIT license.
'''Defines several functions that may be useful when working with dicts.'''
import collections
from python_toolbox import cute_iter_tools
from python_toolbox import comparison_tools
def filter_items(d, condition, double=False, force_dict_type=None):
'''
Get new dict with items from `d` that satisfy the `condition` functions.
`condition` is a function that takes a key and a value.
The newly created dict will be of the same class as `d`, e.g. if you passed
an ordered dict as `d`, the result will be an ordered dict, using the
correct order.
Specify `double=True` to get a tuple of two dicts instead of one. The
second dict will have all the rejected items.
'''
# todo future: possibly shallow-copy `d` to allow for dict classes that
# have more state, (like default factory.)
if force_dict_type is not None:
dict_type = force_dict_type
else:
dict_type = type(d) if (type(d).__name__ != 'dictproxy') else dict
if double:
return map(
dict_type,
cute_iter_tools.double_filter(
lambda (key, value): condition(key, value),
d.iteritems()
)
)
else:
return dict_type(
(key, value) for (key, value) in d.iteritems() if condition(key, value)
)
def get_tuple(d, iterable):
'''Get a tuple of values corresponding to an `iterable` of keys.'''
return tuple(d[key] for key in iterable)
def get_contained(d, container):
'''Get a list of the values in the dict whose keys are in `container`.'''
return [value for (key, value) in d.iteritems() if (key in container)]
def fancy_string(d, indent=0):
'''Show a dict as a string, slightly nicer than dict.__repr__.'''
small_space = ' ' * indent
big_space = ' ' * (indent + 4)
huge_space = ' ' * (indent + 8)
def show(thing, indent=0):
space = ' ' * indent
enter_then_space = '\n' + space
return repr(thing).replace('\n', enter_then_space)
temp1 = (
(big_space + repr(key) + ':\n' + huge_space + show(value, indent + 8))
for (key, value) in d.items())
temp2 = small_space + '{\n' + ',\n'.join(temp1) + '\n' + small_space +'}'
return temp2
def reverse_with_set_values(d, sort=False):
'''
Reverse the dict, with the values of the new dict being sets.
Example:
reverse_with_set_values({1: 2, 3: 4, 'meow': 2}) == \
{2: {1, 'meow'}, 4: {3}}
Instead of a dict you may also input a tuple in which the first item is an
iterable and the second item is either a key function or an attribute name.
A dict will be constructed from these and used.
If you'd like the result dict to be sorted, pass `sort=True`, and you'll
get a sorted `OrderedDict`. You can also specify the sorting key function
or attribute name as the `sort` argument.
'''
### Pre-processing input: #################################################
# #
if hasattr(d, 'items'): # `d` is a dict
fixed_dict = d
else: # `d` is not a dict
assert cute_iter_tools.is_iterable(d) and len(d) == 2
iterable, key_function_or_attribute_name = d
assert cute_iter_tools.is_iterable(iterable)
key_function = comparison_tools.process_key_function_or_attribute_name(
key_function_or_attribute_name
)
fixed_dict = {key: key_function(key) for key in iterable}
# #
### Finished pre-processing input. ########################################
new_dict = {}
for key, value in fixed_dict.iteritems():
if value not in new_dict:
new_dict[value] = []
new_dict[value].append(key)
# Making into sets:
for key, value in new_dict.copy().iteritems():
new_dict[key] = set(value)
if sort:
from python_toolbox import nifty_collections
ordered_dict = nifty_collections.OrderedDict(new_dict)
if isinstance(sort, (collections.Callable, basestring)):
key_function = comparison_tools. \
process_key_function_or_attribute_name(sort)
else:
assert sort is True
key_function = None
ordered_dict.sort(key_function)
return ordered_dict
else:
return new_dict
def devour_items(d):
'''Iterator that pops (key, value) pairs from `d` until it's empty.'''
while d:
yield d.popitem()
def devour_keys(d):
'''Iterator that pops keys from `d` until it's exhaused (i.e. empty).'''
while d:
key = next(d.iterkeys())
del d[key]
yield key
def sum_dicts(dicts):
'''
Return the sum of a bunch of dicts i.e. all the dicts merged into one.
If there are any collisions, the latest dicts in the sequence win.
'''
result = {}
for dict_ in dicts:
result.update(dict_)
return result
def remove_keys(d, keys_to_remove):
'''
Remove keys from a dict.
`keys_to_remove` is allowed to be either an iterable (in which case it will
be iterated on and keys with the same name will be removed), a container
(in which case this function will iterate over the keys of the dict, and if
they're contained they'll be removed), or a filter function (in which case
this function will iterate over the keys of the dict, and if they pass the
filter function they'll be removed.)
If key doesn't exist, doesn't raise an exception.
'''
if isinstance(keys_to_remove, collections.Iterable):
for key in keys_to_remove:
try:
del d[key]
except KeyError:
pass
else:
if isinstance(keys_to_remove, collections.Container):
filter_function = lambda value: value in keys_to_remove
else:
assert isinstance(keys_to_remove, collections.Callable)
filter_function = keys_to_remove
for key in list(d.keys()):
if filter_function(key):
del d[key]
def get_sorted_values(d, key=None):
'''
Get the values of dict `d` as a `tuple` sorted by their respective keys.
'''
kwargs = {'key': key,} if key is not None else {}
return get_tuple(d, sorted(d.keys(), **kwargs))