Difference in syntax of a normal dictionary and OrderedDictonary - python

The syntax of printing a normal dictionary is
{'a': 1, 'c': 3, 'b': 2}
Whereas
The syntax of printing a OrderedDict is
OrderedDict([('a', 1), ('b', 2), ('c', 3)])
Is there any way where i can print/return the OrderedDict in the Normal Dictionary way??

Use a custom __str__ method in your own OrderedDict class ; in the custom method, you can build the string you want:
from collections import OrderedDict
class MyOrderedDict(OrderedDict):
def __str__(self):
return "{%s}" % ", ".join([repr(k)+": "+str(v) for k, v in self.iteritems()])
d = MyOrderedDict([('a', 1), ('b', 2), ('c', 3)])

The easy way:
from collections import OrderedDict
a = {'a': 1, 'c': 3, 'b': 2}
new_dict = dict(OrderedDict(a))
print(new_dict)
print(type(new_dict))
Output:
{'a': 1, 'c': 3, 'b': 2}
<type 'dict'>
The hard way:
You can also return the OrderedDict to a simple dict using groupby from itertools module like this way:
from collections import OrderedDict
from itertools import groupby
a = {'a': 1, 'c': 3, 'b': 2}
new_dict = {key:list(val)[0][1] for key, val in groupby(OrderedDict(a).items(), lambda x : x[0])}
print(new_dict)
print(type(new_dict))
Output:
{'c': 3, 'a': 1, 'b': 2`}
<type 'dict'>
Edit:
I see that there is some downvotes because they think that the output should be an ordered string. So, this is how do deal with it:
from collections import OrderedDict
a = {'a': 1, 'c': 3, 'b': 2}
new_dict = OrderedDict(a)
b = "{%s}" % ", ".join([str(key)+":"+str(val) for key, val in sorted(new_dict.iteritems())])
print(b)
print(type(b))
Output:
{'a':1, 'b':2, 'c':3}
<type 'str'>

To print OrderedDict in a dict-way you can act like this (of course, if the actual order of the items does not matter to print):
def some_function():
d = {'banana': 3, 'apple': 4, 'pear': 1, 'orange': 2}
od = OrderedDict(sorted(d.items(), key=lambda t: t[0]))
return od
my_ordered_dict_variable = some_function()
print('My Ordered Dicdict:', dict(my_ordered_dict_variable))
This code will print:
{'banana': 3, 'apple': 4, 'pear': 1, 'orange': 2}

Related

Convert a list comprehension to functional programming

I have a list of dictionaries
lst = [{'a': (1, 2, 3), 'b': (2, 3)},
{'c': (3, 6), 'd': (4, 8), 'e': (5, 10)},
{'d': (6, 12), 'e': (7, 14)}]
For each key in each dictionary, I want to keep only the first element of the values. So the desired output is
[{'a': 1, 'b': 2}, {'c': 3, 'd': 4, 'e': 5}, {'d': 6, 'e': 7}]
I can get it using a list comprehension like
[{key: val[0] for key, val in dct.items()} for dct in lst]
However, I want to know if it's possible to get the same output using map, itemgetter, itertools, functools etc. What I have so far:
map(dict.values, lst)
But I don't know how to go from here.
For nested iterations, I don't think we can do it without the help of lambda expressions:
from operator import itemgetter, methodcaller
list(map(
lambda items: dict(zip(
map(itemgetter(0), items),
map(itemgetter(0), map(itemgetter(1), items))
)), map(methodcaller('items'), lst)))
# [{'a': 1, 'b': 2}, {'c': 3, 'd': 4, 'e': 5}, {'d': 6, 'e': 7}]
I have to say it's very ugly.
Update:
I found a way to avoid lambda:
First, according to the comment area, we simplify the above expression (the outermost list is omitted here to reduce the difficulty of understanding):
func = lambda d: dict(zip(d, map(itemgetter(0), d.values())))
map(func, lst)
It is easy to observe that dict can be moved outside lambda. We just need to add another map:
func = lambda d: zip(d, map(itemgetter(0), d.values()))
map(dict, map(func, lst))
Similarly, we can move the zip outside lambda:
func = lambda d: map(itemgetter(0), d.values())
map(dict, map(zip, lst, map(func, lst)))
This seems to be the end, and there seems to be no way to convert lambda into a combination of multiple built-in functions, but there are still ways, let's first try to move d.values outside lambda. Here, since the element type of the list is determined, we directly use dict.values instead of operator.methodcaller:
func = lambda values: map(itemgetter(0), values)
map(dict, map(zip, lst, map(func, map(dict.values, lst))))
The answer is ready to come out. We can eliminate lambda by using functools.partial:
map(dict, map(zip, lst, map(partial(map, itemgetter(0)), map(dict.values, lst))))
Test:
>>> from operator import itemgetter
>>> from functools import partial
>>> lst = [{'a': (1, 2, 3), 'b': (2, 3)},
... {'c': (3, 6), 'd': (4, 8), 'e': (5, 10)},
... {'d': (6, 12), 'e': (7, 14)}]
>>> map(dict, map(zip, lst, map(partial(map, itemgetter(0)), map(dict.values, lst))))
<map object at 0x000002A0542CBB20>
>>> list(_)
[{'a': 1, 'b': 2}, {'c': 3, 'd': 4, 'e': 5}, {'d': 6, 'e': 7}]

Merge dictionary with recurrence

I have a loop:
all_rec = {}
for i in range(0, 2):
rec = buildings[best_buildings[i]]['recommendations']
The value of 'rec' after first iteration:
rec = {'a': {'b': 2, 'c': 1}, 'd': {'e': 5}}
The value of 'rec' after second iteration:
rec = {'d': {'e': 4}}
The data type of 'recommendations' is a dictionary. How can I add all the 'rec' dictionaries resulting from this loop into one dictionary 'all_rec'?
I want 'all_rec' to look like this:
all_rec = {'a': {'b': 2, 'c': 1}, 'd': {'e': 5}, 'd': {'e': 4}}
Keeping both 'd': {'e': 5} and 'd': {'e': 4} in the new dictionary. How can I do that in Python 2.7?
If you need to keep all such values, it suggests that the values in the lower dictionaries should be lists, at least for the summary list:
all_rec = {}
for i in range(0, 2):
rec = buildings[best_buildings[i]]['recommendations']
for dk, dc in rec.items():
if dk not in all_rec:
all_rec[dk] = {k:[v] for k,v in dc.items()}
else:
for k,v in dc.items():
if k in all_rec[dk]:
all_rec[dk][k].append(v)
else:
all_rec[dk][k] = [v]
print(all_rec)
{'a': {'b': [2], 'c': [1]}, 'd': {'e': [5,4]}}
This is Python 3.7; I don't know how you'd need to adjust to use 2.7 but I kept it as basic as possible.
I do not have your data but this is the rough idea:
If you want to sum the recommentations:
from collections import defaultdict
all_rec = defaultdict(lambda: defaultdict(int))
for i in range(0, 2):
rec[buildings[best_buildings[i]]] += buildings[best_buildings[i]]['recommendations']
If you want to append the recommendations:
from collections import defaultdict
all_rec = defaultdict(lambda: defaultdict(list))
for i in range(0, 2):
rec[buildings[best_buildings[i]]].append(buildings[best_buildings[i]]['recommendations'])
---
DEMO
>>> from collections import defaultdict
>>>
>>> demo = defaultdict(lambda: defaultdict(int))
>>> demo['a']['b'] += 1
>>> demo
defaultdict(<function <lambda> at 0x7f931a2d2700>, {'a': defaultdict(<class 'int'>, {'b': 1})})
>>> demo['a']['b'] += 1
>>> demo
defaultdict(<function <lambda> at 0x7f931a2d2700>, {'a': defaultdict(<class 'int'>, {'b': 2})})
>>>
>>> demo = defaultdict(lambda: defaultdict(list))
>>> demo['a']['b'].append(1)
>>> demo
defaultdict(<function <lambda> at 0x7f931a2d2b80>, {'a': defaultdict(<class 'list'>, {'b': [1]})})
>>> demo['a']['b'].append(1)
>>> demo
defaultdict(<function <lambda> at 0x7f931a2d2b80>, {'a': defaultdict(<class 'list'>, {'b': [1, 1]})})
>>>

how to reconfigure this dictionary to change its keys

Let's say I have this dictionary:
>>> dic = {('a', 'l'):3, ('a', 'p'):2, ('b', 'l'):4, ('b', 'p'):1}
How can I edit it so I can have it like this:
>>> dic_new = {'a':{'l':3, 'p':2}, 'b':{'l':4, 'p':1}}
Whenever I change the keys I get an error. I am confused.
In each case, you want to set d2[k1][k2]=v whereever you have d1[k1,k2]=v. The simplest way to do this is to start with a defaultdict.
>>> from collections import defaultdict
>>> d1 = {('a', 'l'):3, ('a', 'p'):2, ('b', 'l'):4, ('b', 'p'):1}
>>> d2 = defaultdict(dict)
>>> for k1, k2 in d1:
... d2[k1][k2] = d[k1,k2]
...
>>> d2
defaultdict(<class 'dict'>, {'a': {'l': 3, 'p': 2}, 'b': {'l': 4, 'p': 1}})
>>> dict(d2)
{'a': {'l': 3, 'p': 2}, 'b': {'l': 4, 'p': 1}}
If you don't want to use a defaultdict, use the setdefault method.
d2 = {}
for k1, k2 in d1:
d2.setdefault(k1, {})[k2] = d1[k1,k2]
You can iterate through the original dictionary and create a new one as you find keys:
dic = {('a', 'l'):3, ('a', 'p'):2, ('b', 'l'):4, ('b', 'p'):1}
dic_new = {}
for (new_key, new_sub_key),value in dic.items():
if new_key not in dic_new:
dic_new[new_key] = {}
dic_new[new_key][new_sub_key] = value
print(dic_new)
Output
{'a': {'l': 3, 'p': 2}, 'b': {'l': 4, 'p': 1}}
You can use groupby + OrderedDict:
from itertools import groupby
from collections import OrderedDict
dic = {('a', 'l'):3, ('a', 'p'):2, ('b', 'l'):4, ('b', 'p'):1}
dic = OrderedDict(dic)
new_d = {}
for k, g in groupby(dic, lambda x: x[0]):
for x in g:
if k in new_d:
new_d[k].update({x[1]: dic[x]})
else:
new_d[k] = {x[1]: dic[x]}
print(new_d)
# {'a': {'l': 3, 'p': 2}, 'b': {'l': 4, 'p': 1}}
Or in case where you can guarantee dictionaries are ordered as per first value in tuple key, you can straightaway ignore OrderedDict.

Find summary statistics for a python dictionary with multiple values

suppose I have a dictionary:
a_dic = {'file1':["a","b","c"],
'file2':["b","c","d"],
'file3':["c","d","e"]}
I want to write a function to be able to return a dictionary/dataframe to find the occurrence of the keys like:
occurrence = {'a':1, 'b':2, 'c':3, 'd':2,'e':1}
With collections.Counter object and itertools.chain.from_iterable function:
import collections, itertools
a_dic = {'file1':["a","b","c"], 'file2':["b","c","d"], 'file3':["c","d","e"]}
result = dict(collections.Counter(itertools.chain.from_iterable(a_dic.values())))
print(result)
The output:
{'c': 3, 'e': 1, 'b': 2, 'd': 2, 'a': 1}
from collections import Counter
flat_list = [item for sublist in (a_dic.values()) for item in sublist]
print(Counter(flat_list))
Output
Counter({'c': 3, 'b': 2, 'd': 2, 'a': 1, 'e': 1})

Make Counter.most_common return dictionary

I used the sample from the documentation:
>>> Counter('abracadabra').most_common(3)
[('a', 5), ('r', 2), ('b', 2)]
How can I make the result to be:
{ 'a': 5, 'r' :2 , 'b' :2}
supposing that we want to keep the Counter().most_common() code?
dict will do this easily:
>>> dict(Counter('abracadabra').most_common(3))
{'a': 5, 'r': 2, 'b': 2}
>>>
For further reference, here is part of what is returned by help(dict):
dict(iterable) -> new dictionary initialized as if via:
| d = {}
| for k, v in iterable:
| d[k] = v
The easiest way is to simply use dict()
dict(Counter('abracadabra').most_common(3))
Output:
{'a': 5, 'r': 2, 'b': 2}

Categories