Is there a way to append two dictionaries in Python 2.7? - python

I would like to add two dictionaries and not update first one with second. The value will be a single variable or a list.
What I would like to achieve:
Input:
x = {"a": [1] }
y = {"a": [2, 3], "b": [2] }
Output:
z = {"a": [1,2,3], "b": [2] }
It would be great if it could also accept a single int instead of only list as mentioned earlier:
Input:
x = {"a": 1 }
y = {"a": [2, 3], "b": [2] }
Output:
z = {"a": [1,2,3], "b": [2] }
So looking for
z = append_dicts(x, y)
This is what I came up with.
def merge_two_dicts(x, y):
"""Given two dicts, append them into a new dict."""
for key in x:
if isinstance(x[key], list):
y[key].extend(x[key])
else:
y[key].append(x[key])
return y

A one-liner (works on Python2.7)
dict([(k, x.get(k,[])+y.get(k,[])) for k in set(x)|set(y)])
and a not very pretty one that can digest non-lists
dict([(k, [a for b in [x.get(k,[])]+[y.get(k,[])] for a in (b if isinstance(b, list) else [b])]) for k in set(x)|set(y)])

This can be a solution for you:
def append_dicts(x, y):
z = dict(y)
for k, v in x.items():
if isinstance(v, list):
z[k].extend(v)
if isinstance(v, int):
z[k].append(v)
return z
x = {"a": [1] }
y = {"a": [2, 3], "b": [2] }
print(append_dicts(x, y))
# {'a': [2, 3, 1], 'b': [2]}
x = {"a": 1 }
y = {"a": [2, 3], "b": [2] }
print(append_dicts(x, y))
# {'a': [2, 3, 1], 'b': [2]}

Accumulating functions: one which accepts duplicates and one which does not...
#!/usr/bin/python
from collections import defaultdict
from sets import Set
def combineUnique(input_dict_list):
accumulating_dict = defaultdict(Set)
for d in input_dict_list:
for key,values in d.items():
for value in values:
accumulating_dict[key].add(value)
return accumulating_dict
def combineWithDuplicates(input_dict_list):
accumulating_dict = defaultdict(list)
for d in input_dict_list:
for key,value in d.items():
accumulating_dict[key].extend(value)
return accumulating_dict
if __name__=='__main__':
x = {"a": [1, 2] }
y = {"a": [2, 3], "b": [2] }
print "With duplicates..."
print combineWithDuplicates([x,y])
print "Unique..."
print combineUnique([x,y])
Returns...
With duplicates...
defaultdict(<type 'list'>, {'a': [1, 2, 2, 3], 'b': [2]})
Unique...
defaultdict(<class 'sets.Set'>, {'a': Set([1, 2, 3]), 'b': Set([2])})

Related

Convert each list element into a nested dictionary key

There is this list of string that I need to use to create a nested dictionary with some values ['C/A', 'C/B/A', 'C/B/B']
The output will be in the format {'C': {'A': [1, 2, 3], 'B': {'A': [1, 2, 3], 'B': [1, 2, 3]}}}
I've tried to use the below code to create the nested dictionary and update the value, but instead I get {'C': {'A': [1, 2, 3], 'C': {'B': {'A': [1, 2, 3], 'C': {'B': {'B': [1, 2, 3]}}}}}} as the output which is not the correct format. I'm still trying to figure out a way. any ideas?
s = ['C/A', 'C/B/A', 'C/B/B']
new = current = dict()
for each in s:
lst = each.split('/')
for i in range(len(lst)):
current[lst[i]] = dict()
if i != len(lst)-1:
current = current[lst[i]]
else:
current[lst[i]] = [1,2,3]
print(new)
You can create a custom Tree class:
class Tree(dict):
'''
Create arbitrarily nested dicts.
>>> t = Tree()
>>> t[1][2][3] = 4
>>> t
{1: {2: {3: 4}}}
>>> t.set_nested_item('a', 'b', 'c', value=5)
>>> t
{1: {2: {3: 4}}, 'a': {'b': {'c': 5}}}
'''
def __missing__(self, key):
self[key] = type(self)()
return self[key]
def set_nested_item(self, *keys, value):
head, *rest = keys
if not rest:
self[head] = value
else:
self[head].set_nested_item(*rest, value=value)
>>> s = ['C/A', 'C/B/A', 'C/B/B']
>>> output = Tree()
>>> default = [1, 2, 3]
>>> for item in s:
... output.set_nested_item(*item.split('/'), value=list(default))
>>> output
{'C': {'A': [1, 2, 3], 'B': {'A': [1, 2, 3], 'B': [1, 2, 3]}}}
You do not need numpy for this problem, but you may want to use recursion. Here is a recursive function add that adds a list of string keys lst and eventually a list of numbers to the dictionary d:
def add(lst, d):
key = lst[0]
if len(lst) == 1: # if the list has only 1 element
d[key] = [1, 2, 3] # That element is the last key
return
if key not in d: # Haven't seen that key before
d[key] = dict()
add(lst[1:], d[key]) # The recursive part
To use the function, create a new dictionary and apply the function to each splitter string:
d = dict()
for each in s:
add(each.split("/"), d)
# d
# {'C': {'A': [1, 2, 3], 'B': {'A': [1, 2, 3], 'B': [1, 2, 3]}}}

Merge several dictionaries creating array on different values

So I have a list with several dictionaries, they all have the same keys. Some dictionaries are the same but one value is different. How could I merge them into 1 dictionary having that different values as array?
Let me give you an example:
let's say I have this dictionaries
[{'a':1, 'b':2,'c':3},{'a':1, 'b':2,'c':4},{'a':1, 'b':3,'c':3},{'a':1, 'b':3,'c':4}]
My desired output would be this:
[{'a':1, 'b':2,'c':[3,4]},{'a':1, 'b':3,'c':[3,4]}]
I've tried using for and if nested, but it's too expensive and nasty, and I'm sure there must be a better way. Could you give me a hand?
How could I do that for any kind of dictionary assuming that the amount of keys is the same on the dictionaries and knowing the name of the key to be merged as array (c in this case)
thanks!
Use a collections.defaultdict to group the c values by a and b tuple keys:
from collections import defaultdict
lst = [
{"a": 1, "b": 2, "c": 3},
{"a": 1, "b": 2, "c": 4},
{"a": 1, "b": 3, "c": 3},
{"a": 1, "b": 3, "c": 4},
]
d = defaultdict(list)
for x in lst:
d[x["a"], x["b"]].append(x["c"])
result = [{"a": a, "b": b, "c": c} for (a, b), c in d.items()]
print(result)
Could also use itertools.groupby if lst is already ordered by a and b:
from itertools import groupby
from operator import itemgetter
lst = [
{"a": 1, "b": 2, "c": 3},
{"a": 1, "b": 2, "c": 4},
{"a": 1, "b": 3, "c": 3},
{"a": 1, "b": 3, "c": 4},
]
result = [
{"a": a, "b": b, "c": [x["c"] for x in g]}
for (a, b), g in groupby(lst, key=itemgetter("a", "b"))
]
print(result)
Or if lst is not ordered by a and b, we can sort by those two keys as well:
result = [
{"a": a, "b": b, "c": [x["c"] for x in g]}
for (a, b), g in groupby(
sorted(lst, key=itemgetter("a", "b")), key=itemgetter("a", "b")
)
]
print(result)
Output:
[{'a': 1, 'b': 2, 'c': [3, 4]}, {'a': 1, 'b': 3, 'c': [3, 4]}]
Update
For a more generic solution for any amount of keys:
def merge_lst_dicts(lst, keys, merge_key):
groups = defaultdict(list)
for item in lst:
key = tuple(item.get(k) for k in keys)
groups[key].append(item.get(merge_key))
return [
{**dict(zip(keys, group_key)), **{merge_key: merged_values}}
for group_key, merged_values in groups.items()
]
print(merge_lst_dicts(lst, ["a", "b"], "c"))
# [{'a': 1, 'b': 2, 'c': [3, 4]}, {'a': 1, 'b': 3, 'c': [3, 4]}]
You could use a temp dict to solve this problem -
>>>python3
Python 3.6.9 (default, Nov 7 2019, 10:44:02)
>>> di=[{'a':1, 'b':2,'c':3},{'a':1, 'b':2,'c':4},{'a':1, 'b':3,'c':3},{'a':1, 'b':3,'c':4}]
>>> from collections import defaultdict as dd
>>> dt=dd(list) #default dict of list
>>> for d in di: #create temp dict with 'a','b' as tuple and append 'c'
... dt[d['a'],d['b']].append(d['c'])
>>> for k,v in dt.items(): #Create final output from temp
... ol.append({'a':k[0],'b':k[1], 'c':v})
...
>>> ol #output
[{'a': 1, 'b': 2, 'c': [3, 4]}, {'a': 1, 'b': 3, 'c': [3, 4]}]
If the number of keys in input dict is large, the process to extract
tuple for temp_dict can be automated -
if the keys the define condition for merging are known than it can be simply a constant tuple eg.
keys=('a','b') #in this case, merging happens over these keys
If this is not known at until runtime, then we can get these keys using zip function and set difference, eg.
>>> di
[{'a': 1, 'b': 2, 'c': 3}, {'a': 1, 'b': 2, 'c': 4}, {'a': 1, 'b': 3, 'c': 3}, {'a': 1, 'b': 3, 'c': 4}]
>>> key_to_ignore_for_merge='c'
>>> keys=tuple(set(list(zip(*zip(*di)))[0])-set(key_to_ignore_for_merge))
>>> keys
('a', 'b')
At this point, we can use map to extract tuple for keys only-
>>> dt=dd(list)
>>> for d in di:
... dt[tuple(map(d.get,keys))].append(d[key_to_ignore_for_merge])
>>> dt
defaultdict(<class 'list'>, {(1, 2): [3, 4], (1, 3): [3, 4]})
Now, to recreate the dictionary from default_dict and keys will require some zip magic again!
>>> for k,v in dt.items():
... dtt=dict(tuple(zip(keys, k)))
... dtt[key_to_ignore_for_merge]=v
... ol.append(dtt)
...
>>> ol
[{'a': 1, 'b': 2, 'c': [3, 4]}, {'a': 1, 'b': 3, 'c': [3, 4]}]
This solution assumes that you only know the keys that can be different (eg. 'c') and rest is all runtime.

dictionary of key and list - remove key if any value in list is none

i have dictionary in following format,
mydict =
{ a: [1, 2],
b: [2, 2],
c: [1, 0],
d: [1, 1]
}
if any value is None
{ a: [1, None],
b: [2, 2],
c: [1, 0],
d: [1, 1]
}
I want to remove that key:value pair.
the output should be
{
b: [2, 2],
c: [1, 0],
d: [1, 1]
}
i am printing it like this,
for key, values in mydict.items():
print key, values
.
.
.
I want to remove None without starting a new loop within my for loop so i tried this,
I tried this,
for key, values in mydict.items() if values.items is not None:
but it keep giving me invalid syntax error,
SyntaxError: invalid syntax
Use a dictionary comprehension:
d = { 'a': [1, None],
'b': [2, 2],
'c': [1, 0],
'd': [1, 1] }
print({k:v for k, v in d.items() if None not in v})
# {'b': [2, 2], 'c': [1, 0], 'd': [1, 1]}
If you need a loop like yours:
for key, value in d.items():
if None not in value:
print(key, value)
# do your calculations here
change
for key, values in mydict.items():
print key, values
into
for key, values in mydict.items():
if not None in values:
print key, values
You can try
mydict = { 'a': [1, None],
'b': [2, 2],
'c': [1, 0],
'd': [1, 1]
}
from copy import copy
newdict = copy(mydict)
for key, values in mydict.items():
if None in values:
newdict.pop(key)
print newdict
Try using filter+lambda:
d=dict(filter(lambda x: None not in x[1], list(d.items())))
print(d)
Or with a for loop:
newd = {}
for k,v in d.items():
if not None in v:
newd.update({k:v})
Using dictionary comprehension
your_dict = {key:value for key,value in my_dict.items() if None not in value}
Output:
{'b': [2, 2], 'c': [1, 0], 'd': [1, 1]}

Convert a dictionary of dictionaries to dictionary of lists

I have a dictionary of dictionaries:
d = {"a": {"x":1, "y":2, "z":3}, "b": {"x":2, "y":3, "z":4}, "c": {"x":3, "y":4, "z":5}}
And I want to convert it to:
new_d = {"x":[1, 2, 3], "y": [2, 3, 4], "z": [3, 4, 5]}
The requirement is that new_d[key][i] and new_d[another_key][i] should be in the same sub-dictionary of d.
So I created new_d = {} and then:
for key in d.values()[0].keys():
new_d[key] = [d.values()[i][key] for i in range(len(d.values()))]
This gives me what I expected, but I am just wondering if there are some built-in functions for this operation or there are better ways to do it.
There is no built-in function for this operation, no. I'd just loop over values directly:
new_d = {}
for sub in d.itervalues(): # Python 3: use d.values()
for key, value in sub.iteritems(): # Python 3: use d.items()
new_d.setdefault(key, []).append(value)
This avoids creating a new list for the dict.values() call each time.
Note that dictionaries have no order. The values in the resulting lists are going to fit your criteria however; they'll be added in the same order for each of the keys in new_d:
>>> d = {"a": {"x":1, "y":2, "z":3}, "b": {"x":2, "y":3, "z":4}, "c": {"x":3, "y":4, "z":5}}
>>> new_d = {}
>>> for sub in d.values():
... for key, value in sub.items():
... new_d.setdefault(key, []).append(value)
...
>>> new_d
{'x': [1, 2, 3], 'y': [2, 3, 4], 'z': [3, 4, 5]}
List Comprehension Method
If you like dictionary and list comprehensions ...
d1 = {"a": {"x": 1, "y": 2, "z": 3},
"b": {"x": 2, "y": 3, "z": 4},
"c": {"x": 3, "y": 4, "z": 5}}
dl1 = {kl: [v for di in d1.values() for k, v in di.items() if k == kl]
for di in d1.values() for kl in di.keys()}
print(dl1)
And yields the results hoped for ...
{'x': [1, 2, 3], 'y': [2, 3, 4], 'z': [3, 4, 5]}

Python list to dictionnary with indexes

I am trying to convert a list :
[A, B, A, A, B, C]
to a dictionnary with each item and the indexes where it was found :
{ A : [0,2,3], B : [1,4], C : [5] }
Any idea of an efficient way to do that ?
Use a defaultdict and enumerate:
>>> lst = ['a','b','a','a','b','c']
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> for i, value in enumerate(lst):
... d[value].append(i)
...
>>> d
defaultdict(<class 'list'>, {'a': [0, 2, 3], 'c': [5], 'b': [1, 4]})
Or, this can be accomplished with a plain dict, although, it is usually slower:
>>> lst = ['a','b','a','a','b','c']
>>> d = {}
>>> for i, value in enumerate(lst):
... d.setdefault(value, []).append(i)
...
>>> d
{'a': [0, 2, 3], 'c': [5], 'b': [1, 4]}
You could have, of course, converted the defaultdict to a dict:
>>> d
defaultdict(<class 'list'>, {'a': [0, 2, 3], 'c': [5], 'b': [1, 4]})
>>> dict(d)
{'a': [0, 2, 3], 'c': [5], 'b': [1, 4]}
>>> help(dict)
Try this,
lst = ['A', 'B', 'A', 'A', 'B', 'C']
print {i:[j[0] for j in enumerate(lst) if j[1] == i] for i in set(lst)}
Result
{'A': [0, 2, 3], 'B': [1, 4], 'C': [5]}
Use list comprehension and a dict comprehension. Create a set out of the list first. Then you can easily use enumerate and do this.
>>> l = ["A", "B", "A", "A", "B", "C"]
>>> {i:[j for j,k in enumerate(l) if k==i] for i in set(l)}
{'C': [5], 'B': [1, 4], 'A': [0, 2, 3]}

Categories

Resources