1 """Operations on dictionaries."""
2
3
4 __version__ = "$Revision: 1.9 $"
5
7 """Passes through items listed in l.
8 @param l: list of keys to preserve
9 @type l: list
10 @param d: dictionary to copy/modify.
11 @type d: mapping
12 @return: a dictionary where all keys not listed in l are removed;
13 all entries listed in l are copied into the (new)
14 output dictionary.
15 @rtype: dict
16 """
17
18
19
20
21 return dict( [ (t, d[t]) for t in l ] )
22
23
25 """Removes items listed in l.
26 @param l: list of keys to remove
27 @type l: list
28 @param d: dictionary to copy/modify.
29 @type d: mapping
30 @return: a dictionary where all keys listed in l are removed;
31 all entries not listed in l are copied into the (new)
32 output dictionary.
33 @rtype: dict
34 """
35 o = d.copy()
36 for t in l:
37 if o.has_key(t):
38 del o[t]
39 return o
40
41
43 """@return: a dictionary containing those key=value pairs that are common to all the dictionaries.
44 @rtype: dict
45 @param dlist: dictionaries.
46 @type dlist: list(dict)
47 """
48 rv = dlist[0].copy()
49 for d in dlist[1:]:
50 dellist = []
51 for (k, v) in rv.items():
52 try:
53 tmp = d[k]
54 except KeyError:
55 dellist.append(k)
56 continue
57 if tmp != v:
58 dellist.append(k)
59 for k in dellist:
60 del rv[k]
61 return rv
62
63
65 class foo(object):
66 pass
67 d1 = {'a': 'b', 'b': 'x', 'c': None, 'd': foo()}
68 d2 = {'a': 'b', 'b': 'y'}
69 d3 = {'a': 'b', 'c': None}
70 assert intersection(d1, d2) == {'a': 'b'}
71 assert intersection(d1, {}) == {}
72 assert intersection(d1, d3) == {'a': 'b', 'c':None}
73 d4 = {'d': foo()}
74 assert intersection(d1, d4) == {}
75
76
78 """Reverse a 1-1 mapping so it maps values to keys
79 instead of keys to values.
80 @type d: dict
81 @rtype: dict
82 @return: M such that if m[k]=v, M[v]=k
83 @raise ValueError: if the mapping is many-to-one.
84 """
85 o = {}
86 for (k, v) in d.items():
87 if v in o:
88 raise ValueError, 'Dictionary is not 1 to 1 mapping'
89 o[v] = k
90 return o
91
92
94 """Compose two mappings (dictionaries) into one.
95 @return: C{d} such that d[k] = d2[d1[k]]
96 @rtype: L{dict}
97 @param d2: second mapping
98 @param d1: first mapping
99 """
100 o = {}
101 for (k, v) in d1.items():
102 try:
103 o[k] = d2[d1[k]]
104 except KeyError:
105 pass
106 return o
107
108
115
116
118 """Read in a dictionary from a two-column file;
119 columns are separated by whitespace.
120 This is not a completely general format, because keys cannot contain
121 whitespace and the values cannot start or end in whitespace.
122
123 The format can contain comments at the top; these are
124 lines that begin with a hash mark (#). Empty lines are
125 ignored.
126 @param f: an open L{file}
127 @return: a dictionary and a list of comments
128 @rtype: tuple(dict(str:str), list(str))
129 @note: You can get into trouble if you have a key that starts with
130 a hash mark (#). If it happens to be the first key,
131 and if there are no comments or if it directly follows a
132 comment, it will be interpreted as a comment.
133 To avoid this possibility, begin your file with a comment
134 line, then a blank line.
135 """
136 o = {}
137 comments = []
138 in_hdr = True
139 n = 0
140 for l in f:
141 n += 1
142 if in_hdr:
143 if l.startswith('#'):
144 comments.append(l[1:].strip())
145 continue
146 in_hdr = False
147 if l == '\n':
148 continue
149 if not l.endswith('\n'):
150 raise BadFileFormatError("Last line is incomplete: %s" % getattr(f, "name", "???"))
151 try:
152 k, v = l.strip().split(None, 1)
153 except ValueError:
154 raise BadFileFormatError("File %s line %d: too few columns" %
155 (getattr(f, "name", "???"), n)
156 )
157 if k in o:
158 raise BadFileFormatError("File contains duplicate keys: %s on line %d and earlier" %
159 (repr(k), n)
160 )
161 o[k] = v
162 return o, comments
163
164
166 """For a dictionary of key=value pairs, return the key with the largest value.
167 @note: When there are several equal values, this will return a single, arbitrary choice.
168 @raise ValueError: when the input dictionary is empty.
169 """
170 kmax = None
171 vmax = None
172 firstpass = True
173 for (k, v) in d.items():
174 if firstpass or v>vmax:
175 vmax = v
176 firstpass = False
177 kmax = k
178 if firstpass:
179 raise ValueError, "argmax is not defined for an empty dictionary"
180 return kmax
181
182
184 """OBSOLETE: replace with class L{dict_of_lists}.
185 Add an entry to a dictionary of lists.
186 A new list is created if necessary; else the value is
187 appended to an existing list.
188 Obsolete: replace with dict_of_lists.
189 """
190 try:
191 dict[key].append(value)
192 except KeyError:
193 dict[key] = [ value ]
194
195
197 """OBSOLETE: replace with class L{dict_of_accums}.
198 Add an entry to a dictionary of counters.
199 A new counter is created if necessary; else the value is
200 added to an existing counter.
201 Obsolete: replace with dict_of_accums.
202 """
203 try:
204 dict[key] += value
205 except KeyError:
206 dict[key] = value
207
208
210 """A dictionary of lists."""
211
212 - def add(self, key, value):
213 """Append value to the list indexed by key."""
214 try:
215 self[key].append(value)
216 except KeyError:
217 self[key] = [ value ]
218
220 """Append values to the list indexed by key."""
221 try:
222 self[key].extend(values)
223 except KeyError:
224 self[key] = values
225
227 """Append value to the list indexed by key."""
228 try:
229 v = self[key]
230 except KeyError:
231 self[key] = [value]
232 else:
233 if value not in v:
234 v.append( value )
235
237 """This does a shallow copy."""
238 return dict_of_lists(self)
239
240
242 for (k, v) in other:
243 self.addgroup(k, v)
244
245
247 """A dictionary of lists."""
248 - def add(self, key, value):
249 """Append value to the set indexed by key."""
250 try:
251 self[key].add(value)
252 except KeyError:
253 self[key] = set([value])
254
256 """Append values to the list indexed by key."""
257 try:
258 self[key].update(values)
259 except KeyError:
260 self[key] = set(values)
261
262
264 """This does a shallow copy."""
265 return dict_of_sets(self)
266
267
269 for (k, v) in other:
270 self.addgroup(k, v)
271
272
274 """A dictionary of accumulators.
275 Note that the copy() function produces a dict_of_accums."""
276
277 - def add(self, key, value):
278 """Add value to the value indexed by key."""
279 try:
280 self[key] += value
281 except KeyError:
282 self[key] = value
283
284
287
288
290 for (k, v) in other:
291 self.add(k, v)
292
293
295 """A dictionary of accumulators.
296 Note that the copy() function produces a dict_of_averages."""
297
298 - def add(self, key, value, weight=1.0):
299 """Add value to the value indexed by key."""
300 assert weight >= 0.0
301 if key in self:
302 self[key] += complex(value*weight, weight)
303 else:
304 self[key] = complex(value*weight, weight)
305
307 vw = self[key]
308 assert vw.imag > 0.0, "Weight for key=%s is nonpositive(%g)" % (key, vw.imag)
309 return vw.real/vw.imag
310
312 o = {}
313 for (k, vw) in self.items():
314 assert vw.imag > 0.0, "Weight for key=%s is nonpositive(%g)" % (k, vw.imag)
315 o[k] = vw.real/vw.imag
316 return o
317
319 vw = self[key]
320 return (vw.real, vw.imag)
321
324
325
327 for (k, v) in other:
328 self.add(k, v)
329
330
332 """A dictionary of maxima. Add a new number and the stored
333 maximum will increase iff the new value is bigger.
334 Note that the copy() function produces a dict_of_accums."""
335
336 - def add(self, key, value):
337 """Add value to the value indexed by key."""
338 try:
339 tmp = self[key]
340 if value > tmp:
341 self[key] = value
342 except KeyError:
343 self[key] = value
344
345
348
349
351 for (k, v) in other:
352 self.add(k, v)
353
354
355
357 """A dictionary of arbitrary things.
358 Note that the copy() function produces a dict_of_X.
359 """
360
361 - def __init__(self, constructor, incrementer):
362 self.constructor = constructor
363 self.incrementer = incrementer
364
365 - def add(self, key, *value):
366 """Add value to the thing indexed by key."""
367 try:
368 self.incrementer(self[key], *value)
369 except KeyError:
370 self[key] = self.constructor(*value)
371
372
376
378 for d in self:
379 try:
380 if d[key]==value:
381 return d
382 except KeyError:
383 pass
384
385
386
389
390 if __name__ == '__main__':
391 test()
392