How to initialize defaultdict with keys? - python

I have a dictionary of lists, and it should be initialized with default keys. I guess, the code below is not good (I mean, it works, but I don't feel that it is written in the pythonic way):
d = {'a' : [], 'b' : [], 'c' : []}
So I want to use something more pythonic like defaultict:
d = defaultdict(list)
However, every tutorial that I've seen dynamically sets the new keys. But in my case all the keys should be defined from the start. I'm parsing other data structures, and I add values to my dictionary only if specific key in the structure also contains in my dictionary.
How can I set the default keys?

From the comments, I'm assuming you want a dictionary that fits the following conditions:
Is initialized with set of keys with an empty list value for each
Has defaultdict behavior that can initialize an empty list for non-existing keys
#Aaron_lab has the right method, but there's a slightly cleaner way:
d = defaultdict(list,{ k:[] for k in ('a','b','c') })

That's already reasonable but you can shorten that up a bit with a dict comprehension that uses a standard list of keys.
>>> standard_keys = ['a', 'b', 'c']
>>> d1 = {key:[] for key in standard_keys}
>>> d2 = {key:[] for key in standard_keys}
>>> ...

If you're going to pre-initialize to empty lists, there is no need for a defaultdict. Simple dict-comprehension gets the job done clearly and cleanly:
>>> {k : [] for k in ['a', 'b', 'c']}
{'a': [], 'b': [], 'c': []}

If you have a close set of keys (['a', 'b', 'c'] in your example)
you know you'll use, you can definitely use the answers above.
BUT...
dd = defaultdict(list) gives you much more then: d = {'a':[], 'b':[], 'c':[]}.
You can append to "not existing" keys in defaultdict:
>>dd['d'].append(5)
>>dd
>>defaultdict(list, {'d': 5})
where if you do:
>>d['d'].append(5) # you'll face KeyError
>>KeyError: 'd'
Recommend to do something like:
>>d = {'a' : [], 'b' : [], 'c' : []}
>>default_d = defaultdict(list, **d)
now you have a dict holding your 3 keys: ['a', 'b', 'c'] and empty lists as values, and you can also append to other keys without explicitly writing: d['new_key'] = [] before appending

You can have a function defined which will return you a dict with preset keys.
def get_preset_dict(keys=['a','b','c'],values=None):
d = {}
if not values:
values = [[]]*len(keys)
if len(keys)!=len(values):
raise Exception('unequal lenghts')
for index,key in enumerate(keys):
d[key] = values[index]
return d
In [8]: get_preset_dict()
Out[8]: {'a': [], 'b': [], 'c': []}
In [18]: get_preset_dict(keys=['a','e','i','o','u'])
Out[18]: {'a': [], 'e': [], 'i': [], 'o': [], 'u': []}
In [19]:
get_preset_dict(keys=['a','e','i','o','u'],values=[[1],[2,2,2],[3],[4,2],[5]])
Out[19]: {'a': [1], 'e': [2, 2, 2], 'i': [3], 'o': [4, 2], 'u': [5]}

from collections import defaultdict
list(map((data := defaultdict(list)).__getitem__, 'abcde'))
data
Out[3]: defaultdict(list, {'a': [], 'b': [], 'c': [], 'd': [], 'e':
[]})

Related

Python how to use defaultdict fromkeys to generate a dictionary with predefined keys and empty lists

Here is the code:
from collections import defaultdict
result = defaultdict.fromkeys(['a','b','c'], list)
result['a'].append(1)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-44-6c01c8d56a42> in <module>()
----> 1 result['a'].append('1')
TypeError: descriptor 'append' requires a 'list' object but received a 'str'
I don't understand the error message, what went wrong and how to fix it?
The .fromkeys method is primarily used to set a dict to a single same default value:
>>> {}.fromkeys(['a','b','c'])
{'a': None, 'c': None, 'b': None}
It does not call a function for each key (which is what list or [] is).
Default dict needs a 'factory function' to be a default dict:
>>> from collections import defaultdict
>>> result=defaultdict(list)
>>> result
defaultdict(<type 'list'>, {})
The factory function (in this case, list) is called any time a missing key is added to a defaultdict to form the default value.
So to set three lists with keys 'a','b','c' you would do:
>>> for e in ('a','b','c'):
... result[e] # 'e' is not in the dict, so it is added
# and a new list is the value
>>> result
defaultdict(<type 'list'>, {'a': [], 'c': [], 'b': []})
Or, you can use the .update method as Raymond Hettinger points out:
>>> result=defaultdict(list)
>>> result.update((k,[]) for k in 'abc')
>>> result
defaultdict(<type 'list'>, {'a': [], 'c': [], 'b': []})
Or, as ivan_pozdeev points out in comments, you can do:
>>> di=defaultdict(list,{k:[] for k in 'abc'})
>>> di
defaultdict(<type 'list'>, {'a': [], 'c': [], 'b': []})
Or you can use a regular Python dict with a dict comprehension to get the same thing -- no defaultdict required -- if you only need or want those three keys with unique lists as their values:
>>> di={k:[] for k in 'abc'}
>>> di
{'a': [], 'c': [], 'b': []}
And those are separate lists for each key:
>>> di['a'].append(1)
>>> di
{'a': [1], 'c': [], 'b': []}
A common mistake (which I have made ;-0) is to use something like .fromkeys and get the same list referred to by multiple keys:
>>> di2={}.fromkeys(['a','b','c'], [])
>>> di2
{'a': [], 'c': [], 'b': []} # looks ok
>>> di2['a'].append('WRONG!')
>>> di2
{'a': ['WRONG!'], 'c': ['WRONG!'], 'b': ['WRONG!']}
Happens because the same single list is referred to by all the keys.
Unfortunately, fromkeys() is for setting the same value over and over again. It isn't helpful when you need distinct lists.
So, I would tackle the problem like this:
>>> keys = ['barry', 'tim', 'fredrik', 'alex']
>>> d = defaultdict(list)
>>> d.update((k, []) for k in keys)
>>> d
defaultdict(<class 'list'>, {'barry': [], 'tim': [], 'fredrik': [], 'alex': []})

Python3 dictionary merge based on values

I have a dictionary composed of {key: value}.
I select a set of keys from this dictionary.
I'd like to build a new dictionary with {keyA: set of all keys wich have the same value as keyA}.
I already have a solution: Is there a faster way to do it?
It seems very slow to me, and I imagine I'm not the only one in this case!
for key1 in selectedkeys:
if key1 not in seen:
seen.add(key1)
equal[key1] = set([key1])#egual to itself
for key2 in selectedkeys:
if key2 not in seen and dico[key1] == dico[key2]:
equal[key1].add(key2)
seen.update(equal[key1])
Try this
>>> a = {1:1, 2:1, 3:2, 4:2}
>>> ret_val = {}
>>> for k, v in a.iteritems():
... ret_val.setdefault(v, []).append(k)
...
>>> ret_val
{1: [1, 2], 2: [3, 4]}
def convert(d):
result = {}
for k, v in d.items(): # or d.iteritems() if using python 2
if v not in result:
result[v] = set()
result[v].add(k)
return result
or just use collections.defaultdict(set) if you are careful enough not to access any non key later :-)
So you want to create a dictionary that maps key to "the set of all keys which have the same value as key" for each selected key in a given source dictionary.
Thus, if the source dictionary is:
{'a': 1, 'b': 2, 'c': 1, 'd': 2, 'e': 3, 'f': 1, 'g': 3)
and the selected keys are a, b, and e, the result should be:
{'a': {'a', 'c', 'f'}, 'e': {'g', 'e'}, 'b': {'b', 'd'}}
One way to achieve this would be to use a defaultdict to build a value to key table, and then use that to build the required result from the specified keys:
from collections import defaultdict
def value_map(source, keys):
table = defaultdict(set)
for key, value in source.items():
table[value].add(key)
return {key: table[source[key]] for key in keys}
source = {'a': 1, 'b': 2, 'c': 1, 'd': 2, 'e': 3, 'f': 1, 'g': 3)
print(value_map(source, ['a', 'b', 'e']))
Output:
{'a': {'a', 'c', 'f'}, 'e': {'g', 'e'}, 'b': {'b', 'd'}}
Since you select a set of keys from the original dictionary. We can modify #Nilesh solution for your purpose.
a = {1:1, 2:1, 3:2, 4:2}
keys = [1, 3] # lets say this is the list of keys
ret_val = {}
for i in keys:
for k,v in a.items():
if a[i]==v:
ret_val.setdefault(i, []).append(k)
print (ret_val)
{1: [1, 2], 3: [3, 4]}
This was sort of stated in the comments by #Patrick Haugh:
d=your dictionary
s=set(d.values())
d2={i:[] for i in s}
for k in d:
d2[d[k]].append(k)

Rearranging levels of a nested dictionary in python

Is there a library that would help me achieve the task to rearrange the levels of a nested dictionary
Eg: From this:
{1:{"A":"i","B":"ii","C":"i"},2:{"B":"i","C":"ii"},3:{"A":"iii"}}
To this:
{"A":{1:"i",3:"iii"},"B":{1:"ii",2:"i"},"C":{1:"i",2:"ii"}}
ie first two levels on a 3 levelled dictionary swapped. So instead of 1 mapping to A and 3 mapping to A, we have A mapping to 1 and 3.
The solution should be practical for an arbitrary depth and move from one level to any other within.
>>> d = {1:{"A":"i","B":"ii","C":"i"},2:{"B":"i","C":"ii"},3:{"A":"iii"}}
>>> keys = ['A','B','C']
>>> e = {key:{k:d[k][key] for k in d if key in d[k]} for key in keys}
>>> e
{'C': {1: 'i', 2: 'ii'}, 'B': {1: 'ii', 2: 'i'}, 'A': {1: 'i', 3: 'iii'}}
thank god for dict comprehension
One way to think about this would be to consider your data as a (named) array and to take the transpose. An easy way to achieve this would be to use the data analysis package Pandas:
import pandas as pd
df = pd.DataFrame({1: {"A":"i","B":"ii","C":"i"},
2: {"B":"i","C":"ii"},
3: {"A":"iii"}})
df.transpose().to_dict()
{'A': {1: 'i', 2: nan, 3: 'iii'},
'B': {1: 'ii', 2: 'i', 3: nan},
'C': {1: 'i', 2: 'ii', 3: nan}}
I don't really care about performance for my application of this so I haven't bothered checking how efficient this is. Its based on bubblesort so my guess is ~O(N^2).
Maybe this is convoluted, but essentially below works by:
- providing dict_swap_index a nested dictionary and a list. the list should be of the format [i,j,k]. The length should be the depth of the dictionary. Each element corresponds to which position you'd like to move each element to. e.g. [2,0,1] would indicate move element 0 to position 2, element 1 to position 0 and element 2 to position 1.
- this function performs a bubble sort on the order list and dict_, calling deep_swap to swap the levels of the dictionary which are being swapped in the order list
- deep_swap recursively calls itself to find the level provided and returns a dictionary which has been re-ordered
- swap_two_level_dict is called to swap any two levels in a dictionary.
Essentially the idea is to perform a bubble sort on the dictionary, but instead of swapping elements in a list swap levels in a dictionary.
from collections import defaultdict
def dict_swap_index(dict_, order):
for pas_no in range(len(order)-1,0,-1):
for i in range(pas_no):
if order[i] > order[i+1]:
temp = order[i]
order[i] = order[i+1]
order[i+1] = temp
dict_ = deep_swap(dict_, i)
return dict_, order
def deep_swap(dict_, level):
dict_ = deepcopy(dict_)
if level==0:
dict_ = swap_two_level_dict(dict_)
else:
for key in dict_:
dict_[key] = deep_swap(dict_[key], level-1)
return dict_
def swap_two_level_dict(a):
b = defaultdict(dict)
for key1, value1 in a.items():
for key2, value2 in value1.items():
b[key2].update({key1: value2})
return b
e.g.
test_dict = {'a': {'c': {'e':0, 'f':1}, 'd': {'e':2,'f':3}}, 'b': {'c': {'g':4,'h':5}, 'd': {'j':6,'k':7}}}
result = dict_swap_index(test_dict, [2,0,1])
result
(defaultdict(dict,
{'c': defaultdict(dict,
{'e': {'a': 0},
'f': {'a': 1},
'g': {'b': 4},
'h': {'b': 5}}),
'd': defaultdict(dict,
{'e': {'a': 2},
'f': {'a': 3},
'j': {'b': 6},
'k': {'b': 7}})}),
[0, 1, 2])

What is the best way to search for a key in multiple dictionaries in Python

I know we can search for a key in Python like this:
if key in myDict:
#Do something here
I know we can extend this and search for the key in multiple dictionaries using elif statement
if key in myDict_1:
#Do something here
elif key in myDict_2:
#Do something here
or by doing
if key in (myDict_1.keys() + myDict_2.keys()):
#Do something here
But is there a more succinct way to search for key in Python in two different dicts without using if-else or adding the list of keys explicitly ?
The answer to your question as written is:
if any(key in d for d in dicts):
# do something
If you need to know which dictionary or dictionaries contain the key, you can use itertools.compress():
>>> d1 = dict(zip("kapow", "squee"))
>>> d2 = dict(zip("bar", "foo"))
>>> d3 = dict(zip("xyz", "abc"))
>>> dicts = d1, d2, d3
>>> from pprint import pprint
>>> pprint(dicts)
({'a': 'q', 'k': 's', 'o': 'e', 'p': 'u', 'w': 'e'},
{'a': 'o', 'b': 'f', 'r': 'o'},
{'x': 'a', 'y': 'b', 'z': 'c'})
>>> from itertools import compress
>>> for d_with_key in compress(dicts, ("a" in d for d in dicts)):
... print(d_with_key)
...
{'a': 'q', 'p': 'u', 'k': 's', 'w': 'e', 'o': 'e'}
{'a': 'o', 'r': 'o', 'b': 'f'}
The correct way would be as Zero wrote:
if any(key in d for d in dicts): # do something
Fixing after reading comments below, thanks to #jwodder:
But you can also create a tuple of the keys of both (or more) dictionaries using the itertools.chain function.
>>> a = {1:2}
>>> b = {3:4}
>>> c = {5:6, 7:8}
>>> print(tuple(itertools.chain(a, b, c)))
(1, 3, 5, 7)
so you also can :
if x in tuple(itertools.chain(a, b, c)):
# Do something
A little list comprehension is also possible here; if you're simply trying to ascertain if a key is in a container of dicts, any() does exactly that; if you want to get the dict (or dicts) back and work with them, perhaps something like this would suffice:
>>> def get_dicts_with_key(some_key, *dicts):
... return [d for d in dicts if some_key in d]
>>> dict1 = {"hey":123}
>>> dict2 = {"wait":456}
>>> get_dicts_with_key('hey', dict1, dict2)
[{'hey': 123}]
>>> get_dicts_with_key('wait', dict1, dict2)
[{'wait': 456}]
>>> get_dicts_with_key('complaint', dict1, dict2)
[]
If the keys were present in either dict, both would be returned, as such:
>>> dict1['complaint'] = 777
>>> dict2['complaint'] = 888
>>> get_dicts_with_key('complaint', dict1, dict2)
[{'complaint': 777, 'hey': 123}, {'complaint': 888, 'wait': 456}]
>>>
Why don't you put your dicts in an iterable like a list and simple loop over then? You can express it as a function like so.
def has_key(key, my_dicts):
for my_dict in my_dicts:
if key in my_dict:
return True
return False
It would be used like so.
>>> dict1 = {'a':1, 'b': 2}
>>> dict2 = {'b':10, 'c': 11}
>>> has_key('b', [dict1, dict2])
True

Make a python nested list for use in Django's unordered_list

I've got a Django model with a self-referencing foreign key, so my model (as a class in its most basic form) looks like:
class MyObj(object):
def __init__(self, id, ttl, pid):
self.id = id
self.name = ttl
self.parentid = pid
So a sample of my data might look like:
nodes = []
nodes.append(MyObj(1,'a',0))
nodes.append(MyObj(2,'b',0))
nodes.append(MyObj(3,'c',1))
nodes.append(MyObj(4,'d',1))
nodes.append(MyObj(5,'e',3))
nodes.append(MyObj(6,'f',2))
I've got to a point where I can convert this into a nested dictionary:
{'a': {'c': {'e': {}}, 'd': {}}, 'b': {'f': {}}}
using Converting tree list to hierarchy dict as a guide, but I need it in a form that I can use for Django's unordered_list filter.
So my question is, how can I get from (either) a nested dictionary to a nested list/tuple or straight from the source data to a nested list? I can't seem to get a recursive function to nest the lists correctly (as in a list I can't reference "sub trees" by name)
eval(string_rep_of_dictionary.replace(':',',').replace('{','[').replace('}',']')) seems to just about get me there but that seems a horrible solution?
Try
lists = {}
for n in nodes:
b = lists.setdefault(n.id, [])
lists.setdefault(n.parentid, []).extend([n.name, b])
print lists[0]
or, using collections.defaultdict
lists = collections.defaultdict(list)
for n in nodes:
lists[n.parentid] += [n.name, lists[n.id]]
print lists[0]
both of which will print
['a', ['c', ['e', []], 'd', []], 'b', ['f', []]]
Edit:To get rid of the empty lists, iterate through the nodes for a second time:
for n in nodes:
if not lists[n.id]:
lists[n.parentid].remove(lists[n.id])
def nested_dict_to_list(d):
result = []
for key, value in d.iteritems():
try:
value = nested_dict_to_list(value)
except AttributeError:
pass
result += [key, value]
return result
test = {'a': {'c': {'e': {}}, 'd': {}}, 'b': {'f': {}}}
desired_result = ['a', ['c', ['e', []], 'd', []], 'b', ['f', []]]
nested_dict_to_list(test) == desired_result
# True

Categories