I have 2 ordered dictionaries, like:
a = collections.OrderedDict()
b = collections.OrderedDict()
And they have stuff in them. How do I merge these 2? I tried:
mergeDict = dict(a.items() + b.items())
but doing this it's not a ordered dictionary anymore.
What I am looking for: if a = {1, 2, 5, 6} and b = [0, 7, 3, 9} then mergeDict = {1, 2, 5, 6, 0, 7, 3, 9}
Two ways (assuming Python 3.6):
Use "update method". Suppose there are two dictionaries:
>>> d1 = collections.OrderedDict([('a', 1), ('b', 2)])
>>> d2 = {'c': 3, 'd': 4}
>>> d1.update(d2)
>>> d1
OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
Second method using 'concatenation operator (+)'
>>> d1 = collections.OrderedDict([('a', 1), ('b', 2)])
>>> d2 = {'c': 3, 'd': 4}
>>> d3 = collections.OrderedDict(list(d1.items()) + list(d2.items()))
>>> d3
OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
from itertools import chain
from collections import OrderedDict
OrderedDict(chain(a.items(), b.items()))
CPython 3.6, and any 3.7+ interpreter already preserves dictionary key ordering.
This allows you to do use the {**a, **b} syntax.
For example:
>>> a = {1: "AA", 2: "BB", 5: "CC", 6: "DD"}
>>> b = {0: "EE", 7: "FF", 3: "GG", 9: "HH"}
>>> {**a, **b}
{1: 'AA', 2: 'BB', 5: 'CC', 6: 'DD', 0: 'EE', 7: 'FF', 3: 'GG', 9: 'HH'}
instead of dict use back OrderedDict for mergeDict
mergeDict = collections.OrderedDict(a.items() + b.items())
Remark :this only works for python 2.x, add list() over dict.items() for python 3 because dict.items() no longer return list that support + operation
or use a.update(b) like #alfasin mentions in comment
i try with simple example and both method works well for me
Related
I want to create a set of dictionaries in python. I know how to make a list of dictionaries and convert it to set, but I want to add a dictionary to a set. Can anyone help me?
I got this error when I try below code:
"Exception has occurred: TypeError unhashable type: 'dict'"
C = set()
A = {'a':1, 'c':2, 'd':3}
B = {'c':3, 'd':4, 2:5 }
C.add(A)
C.add(B)
Thanks
For the below:
C = set()
A = {'a':1, 'c':2, 'd':3}
B = {'c':3, 'd':4, 2:5 }
You could do something like this to combine all the above into a single set. I will use an union operator '|' to achieve this.
{i for i in A.items()} | {i for i in B.items()}
This results to:
{('a', 1), ('c', 2), ('c', 3), ('d', 3), ('d', 4), (2, 5)}
** Extra Information **
If you knew from the start that dictionary B does not contain any of the keys found in dictionary A, you could do something like the below to combine the two dictionaries:
For example:
A = {'a':1, 'c':2, 'd':3}
B = {'e':3, 'f':4, 2:5 }
from collections import Counter
C = Counter(A) + Counter(B)
C equates to the below:
Counter({'a': 1, 'c': 2, 'd': 3, 'e': 3, 'f': 4, 2: 5})
please tell me.
Description
I'd like to update the value of a variable of type OrderedDict by using the update method of dict.
However, after executing the update method, the OrderedDict type of the update target variable is lost, and output as expected can not be done.
Question points:
Is it faulty to lose the type of OrderedDict?
Is there another way to update dict while keeping the type of OrderedDict?
Below is an example of the problem.
from collections import OrderedDict
dic = OrderedDict()
dic['a'] = 1
dic['b'] = OrderedDict()
dic['b']['b1'] = 2
dic['b']['b2'] = 3
dic['b']['b3'] = 4
print(dic)
> OrderedDict([('a', 1), ('b', OrderedDict([('b1', 2), ('b2', 3), ('b3', 4)]))]) # ok
new_dic = {'a': 2, 'b': {'b1': 3, 'b2': 4, 'b3': 5}}
print(new_dic)
> {'a': 2, 'b': {'b1': 3, 'b2': 4, 'b3': 5}}
dic.update(new_dic)
print(dic)
> OrderedDict([('a', 2), ('b', {'b1': 3, 'b2': 4, 'b3': 5})]) # NG: Type has been lost
An update has the effect of a rebinding of the affected keys. What you are doing in short, is:
# ...
dic['b'] = OrderedDict()
# ...
dic['b'] = {'b1': 3, 'b2': 4, 'b3': 5}
# ...
The new value of key 'b' in dic is now the common dict. You are trying to do some nested update that is not provided out of the box. You could implement it yourself along the lines of:
def update(d1, d2):
for k, v in d2.items():
if k in d1 and isinstance(v, dict) and isinstance(d1[k], dict):
update(d1[k], v)
else:
d1[k] = v
Now you can apply it to your case:
update(dic, new_dic)
# OrderedDict([('a', 2), ('b', OrderedDict([('b1', 3), ('b2', 4), ('b3', 5)]))])
change this line
new_dic = {'a': 2, 'b': {'b1': 3, 'b2': 4, 'b3': 5}}
to
new_dic = {'a': 2, 'b': OrderedDict([('b1', 3), ('b2', 4), ('b3', 5)])}
It'd be okay!
from collections import OrderedDict
dic = OrderedDict()
dic['a'] = 1
dic['b'] = OrderedDict()
dic['b']['b1'] = 2
dic['b']['b2'] = 3
dic['b']['b3'] = 4
print(dic)
#> OrderedDict([('a', 1), ('b', OrderedDict([('b1', 2), ('b2', 3), ('b3', 4)]))]) # ok
new_dic = {'a': 2, 'b': {'b1': 3, 'b2': 4, 'b3': 5}}
new_dic['b'] = OrderedDict(new_dic['b'])
print(new_dic)
#> {'a': 2, 'b': OrderedDict([('b1', 3), ('b2', 4), ('b3', 5)])}
dic.update(new_dic)
print(dic)
#> OrderedDict([('a', 2), ('b', OrderedDict([('b1', 3), ('b2', 4), ('b3', 5)]))])
Is there a faster or more "pythonic" way to achieve that:
dicta = {'a':{'a1':1, 'a2':2}, 'b':{'b1': 1, 'b2': 2}}
dictb = {'b':{'b1':1, 'a2':2}, 'c':{'c1': 1, 'c2': 2}}
dictc = {}
dictc.update(dicta)
for outside_key in dictb:
if outside_key in dictc:
for inside_key in dictb[outside_key]:
if inside_key in dictc[outside_key]:
dictc[outside_key][inside_key] += dictb[outside_key][inside_key]
else:
dictc[outside_key][inside_key] = dictb[outside_key][inside_key]
else:
dictc[outside_key] = dictb[outside_key]
dictc now contains the following:
{'a': {'a1': 1, 'a2': 2},
'c': {'c2': 2, 'c1': 1},
'b': {'a2': 2, 'b1': 2, 'b2': 2}}
I'm using this in a django model definition, the above seemed the most obvious way to describe the problem but I then realised that it was not specific enough.
Here is how the django code looks like:
def get_capabilities(self):
capabilities_dict = {}
capabilities_list = ('card', 'module') #Two ManyToMany fields
capabilities_dict.update(self.barebone.get_capabilities())
# self.barebone.get_capabilities() is the only foreingkey field
# and serves as the base on which we build the capabilities list.
for capability in capabilities_list:
instances = getattr(self, capability).all()
for instance in instances:
capabilities = instance.get_capabilities()
for capability_name in capabilities:
if capability_name in capabilities_dict:
for obj in capabilities[capability_name]:
if obj in capabilities_dict[capability_name]:
capabilities_dict[capability_name][obj] += capabilities[capability_name][obj]
else:
capabilities_dict[capability_name][obj] = capabilities[capability_name][obj]
else:
capabilities_dict[capability_name] = capabilities[capability_name]
return capabilities_dict
self.barebone.get_capabilities() looks like this:
{'compatible_bus_types': {<BusType: PCI-X>: 1},
'compatible_storage_interfaces': {<StorageInterface: SATA>: 8},
'compatible_storage_form_factors': {<StorageFormFactor: 3.5">: 4}}
and the function get_capabilities() above returns this:
{'compatible_network_connectors': {},
'compatible_storage_interfaces': {<StorageInterface: SATA>: 8,
<StorageInterface: SAS>: 8},
'compatible_network_standards': {},
'compatible_storage_form_factors': {<StorageFormFactor: 3.5">: 4},
'compatible_bus_types': {<BusType: PCI-X>: 1},
'compatible_network_form_factors': {}}
Each <> enclosed "inner-keys" are in fact another model instance.
Thanks,
Using collections.Counter with a normal dict:
>>> from collections import Counter
>>> dictc = {}
>>> for d in (dicta, dictb):
for k, v in d.items():
dictc[k] = dictc.get(k, Counter()) + Counter(v)
...
>>> dictc
{'a': Counter({'a2': 2, 'a1': 1}),
'c': Counter({'c2': 2, 'c1': 1}),
'b': Counter({'a2': 2, 'b1': 2, 'b2': 2})}
or with a defaultdict:
>>> from collections import Counter, defaultdict
>>> dictc = defaultdict(Counter)
>>> for d in (dicta, dictb):
for k, v in d.items():
dictc[k] += Counter(v)
...
>>> dictc
defaultdict(<class 'collections.Counter'>,
{'a': Counter({'a2': 2, 'a1': 1}),
'c': Counter({'c2': 2, 'c1': 1}),
'b': Counter({'a2': 2, 'b1': 2, 'b2': 2})})
You use nested dicts but I think there is an easier data-structure: You can take a tuple like ('a','a2') as key instead. Your dicts would look like this:
dicta = {('a', 'a1'): 1, ('a', 'a2'): 2, ('b', 'b1'): 1, ('b', 'b2'): 2}
dictb = {('b', 'a2'): 2, ('b', 'b1'): 1, ('c', 'c1'): 1, ('c', 'c2'): 2}
Accessing the values would work like this:
print dicta['a', 'a1']
Out: 1
instead of
print dicta['a']['a1']
Out: 1
To change your data into that structure you can you this function:
def unnestdict(dct):
return { (key1, key2):value
for key1, sub_dct in dct.iteritems()
for key2, value in sub_dct.iteritems()}
If you use this data structure you can easily use the Counter-class from collections-module:
from collections import Counter
counterC = Counter(dicta) + Counter(dictb)
print counterC
Out: Counter({('b', 'b2'): 2, ('a', 'a2'): 2, ('b', 'b1'): 2, ('b', 'a2'): 2, ('c', 'c2'): 2, ('a', 'a1'): 1, ('c', 'c1'): 1})
dictc = dict(counterC)
print dictc
Out: {('b', 'a2'): 2, ('b', 'b1'): 1, ('c', 'c1'): 1, ('c', 'c2'): 2}
It would be convenient if a defaultdict could be initialized along the following lines
d = defaultdict(list, (('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2),
('b', 3)))
to produce
defaultdict(<type 'list'>, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]})
Instead, I get
defaultdict(<type 'list'>, {'a': 2, 'c': 3, 'b': 3, 'd': 4})
To get what I need, I end up having to do this:
d = defaultdict(list)
for x, y in (('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), ('b', 3)):
d[x].append(y)
This is IMO one step more than should be necessary, am I missing something here?
What you're apparently missing is that defaultdict is a straightforward (not especially "magical") subclass of dict. All the first argument does is provide a factory function for missing keys. When you initialize a defaultdict, you're initializing a dict.
If you want to produce
defaultdict(<type 'list'>, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]})
you should be initializing it the way you would initialize any other dict whose values are lists:
d = defaultdict(list, (('a', [1, 2]), ('b', [2, 3]), ('c', [3]), ('d', [4])))
If your initial data has to be in the form of tuples whose 2nd element is always an integer, then just go with the for loop. You call it one extra step; I call it the clear and obvious way to do it.
the behavior you describe would not be consistent with the defaultdicts other behaviors. Seems like what you want is FooDict such that
>>> f = FooDict()
>>> f['a'] = 1
>>> f['a'] = 2
>>> f['a']
[1, 2]
We can do that, but not with defaultdict; lets call it AppendDict
import collections
class AppendDict(collections.MutableMapping):
def __init__(self, container=list, append=None, pairs=()):
self.container = collections.defaultdict(container)
self.append = append or list.append
for key, value in pairs:
self[key] = value
def __setitem__(self, key, value):
self.append(self.container[key], value)
def __getitem__(self, key): return self.container[key]
def __delitem__(self, key): del self.container[key]
def __iter__(self): return iter(self.container)
def __len__(self): return len(self.container)
Sorting and itertools.groupby go a long way:
>>> L = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), ('b', 3)]
>>> L.sort(key=lambda t:t[0])
>>> d = defaultdict(list, [(tup[0], [t[1] for t in tup[1]]) for tup in itertools.groupby(L, key=lambda t: t[0])])
>>> d
defaultdict(<type 'list'>, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]})
To make this more of a one-liner:
L = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), ('b', 3)]
d = defaultdict(list, [(tup[0], [t[1] for t in tup[1]]) for tup in itertools.groupby(sorted(L, key=operator.itemgetter(0)), key=lambda t: t[0])])
Hope this helps
I think most of this is a lot of smoke and mirrors to avoid a simple for loop:
di={}
for k,v in [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2),('b', 3)]:
di.setdefault(k,[]).append(v)
# di={'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]}
If your goal is one line and you want abusive syntax that I cannot at all endorse or support you can use a side effect comprehension:
>>> li=[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2),('b', 3)]
>>> di={};{di.setdefault(k[0],[]).append(k[1]) for k in li}
set([None])
>>> di
{'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]}
If you really want to go overboard into the unreadable:
>>> {k1:[e for _,e in v1] for k1,v1 in {k:filter(lambda x: x[0]==k,li) for k,v in li}.items()}
{'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]}
You don't want to do that. Use the for loop Luke!
>>> kvs = [(1,2), (2,3), (1,3)]
>>> reduce(
... lambda d,(k,v): d[k].append(v) or d,
... kvs,
... defaultdict(list))
defaultdict(<type 'list'>, {1: [2, 3], 2: [3]})
I would like to create a dictionary from list
>>> list=['a',1,'b',2,'c',3,'d',4]
>>> print list
['a', 1, 'b', 2, 'c', 3, 'd', 4]
I use dict() to produce dictionary from list
but the result is not in sequence as expected.
>>> d = dict(list[i:i+2] for i in range(0, len(list),2))
>>> print d
{'a': 1, 'c': 3, 'b': 2, 'd': 4}
I expect the result to be in sequence as the list.
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
Can you guys please help advise?
Dictionaries don't have any order, use collections.OrderedDict if you want the order to be preserved. And instead of using indices use an iterator.
>>> from collections import OrderedDict
>>> lis = ['a', 1, 'b', 2, 'c', 3, 'd', 4]
>>> it = iter(lis)
>>> OrderedDict((k, next(it)) for k in it)
OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
Dictionary is an unordered data structure. To preserve order use collection.OrderedDict:
>>> lst = ['a',1,'b',2,'c',3,'d',4]
>>> from collections import OrderedDict
>>> OrderedDict(lst[i:i+2] for i in range(0, len(lst),2))
OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
You could use the grouper recipe: zip(*[iterable]*n) to collect the items into groups of n:
In [5]: items = ['a',1,'b',2,'c',3,'d',4]
In [6]: items = iter(items)
In [7]: dict(zip(*[items]*2))
Out[7]: {'a': 1, 'b': 2, 'c': 3, 'd': 4}
PS. Never name a variable list, since it shadows the builtin (type) of the same name.
The grouper recipe is easy to use, but a little harder to explain.
Items in a dict are unordered. So if you want the dict items in a certain order, use a collections.OrderedDict (as falsetru already pointed out):
In [13]: collections.OrderedDict(zip(*[items]*2))
Out[13]: OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])