Unpack values from nested dictionaries with depth level = 1 - python

Is there a more elegant way to unpack values from nested dictionaries (depth level = 1) in a set?
d = {1: {10: 'a',
11: 'b'},
2: {20: 'a',
21: 'c'}}
print(set(c for b in [[*set(a.values())] for a in d.values()] for c in b))
# {'a', 'b', 'c'}

You can iterate over values of nested dict and add in set.
d = {1: {10: 'a',
11: 'b'},
2: {20: 'a',
21: 'c'}}
res = set(v for key,val in d.items() for v in val.values())
print(res)
# {'a', 'b', 'c'}

Related

Creating a dictionary from a list of lists using a second list as keys

I have a list of column names, and a list of lists that I would like to turn into a nested dictionary, where each inner list contains the column names as keys. By applying the code below, I encounter the same problem as my real data - I get the right key:value pairs, but only for the very last list.
I thought the way I was trying was a pretty simple approach (too simple?). I'm open to any way to do this, preferably without the use of third party packages, but in the interest of learning would like to know why this doesn't work.
keys = [1, 2, 3]
list_of_lists = [['A', 'B', 'C'], ['D', 'E', 'F']]
for x in list_of_lists:
test = dict(zip(keys, x))
print(test)
Desired output:
{{1: 'A', 2: 'B', 3: 'C'}, {1: 'D', 2: 'E', 3: 'F'}}
Actual output:
{1: 'D', 2: 'E', 3: 'F'}
If what you want is indeed a list of dicts, a very simple one-liner:
keys = [1, 2, 3]
list_of_lists = [['A', 'B', 'C'], ['D', 'E', 'F']]
print([dict(zip(keys, values)) for values in list_of_lists])
# [{1: 'A', 2: 'B', 3: 'C'}, {1: 'D', 2: 'E', 3: 'F'}]
keys = [1, 2, 3]
list_of_lists = [['A', 'B', 'C'], ['D', 'E', 'F']]
test = []
for x in list_of_lists:
test.append(dict(zip(keys, x)))
print(test)
This gives the list of dictionaries.
Output:
[{1: 'A', 2: 'B', 3: 'C'}, {1: 'D', 2: 'E', 3: 'F'}]
Nested dictionaries would require you to have key for each inner element. In the below example, I'm using count as key.
keys = [1, 2, 3]
list_of_lists = [['A', 'B', 'C'], ['D', 'E', 'F']]
test = {}
count = 0
for x in list_of_lists:
test[count] = dict(zip(keys, x))
count = count + 1
print(test)
Output:
{0: {1: 'A', 2: 'B', 3: 'C'}, 1: {1: 'D', 2: 'E', 3: 'F'}}
Unfortunately what you’re desired output shows is a set of dicts, and with a dict being unhashable this will not work.
Alternatively you could make a list or tuple of dicts:
test = [{k:v for k, v in zip(keys, l)} for l in list_of_lists]
#[{1: 'A', 2: 'B', 3: 'C'}, {1: 'D', 2: 'E', 3: 'F'}]
Or a dict of dicts, the keys for the outer dict being an enumeration of the outer list
test = {i: {k:v for k, v in zip(keys, l)} for i, l in enumerate(list_of_lists)}
#{0: {1: 'A', 2: 'B', 3: 'C'}, 1: {1: 'D', 2: 'E', 3: 'F'}}
You are better off placing your dictionaries in a list.
[dict(zip(keys, x)) for x in list_of_lists]
I am not sure, perhaps you would be interested in the order and then you might want to try
{i:x for i, x in zip(range(len(list_of_lists)), list_of_lists)}
Hope this helps
Edit:
Changed the dictionary response code

Update dictionary while condition

I am working on a code that given a dictionary of dictionaries which looks like this:
D = {1: {2: 'a', 3: 'b'}, 10: {11: 'a', 12: 'b'}}
where 1 and 2 are keys while inner dictionaries {2: 'a', 3: 'b'} and {11: 'a', 12: 'b'} are results after applying an addition of 1 or 2. D[1] + 1 = 2 and D[1] + 2 = 3. 'a' and 'b' indicates an addition to 1 or 2 respectively.
From D I'd like to keep applying those additions to its new products which I can get by doing this:
products = list(set([l for x, y in D.items() for l, m in y.items()]))
products = [2,3,11,12]
I use a set list just to avoid apply additions on products that already are on D.
So applying additions to every item in product and add them to D will end up in something like this:
D = {1: {2: 'a', 3: 'b'}, 10: {11: 'a', 12: 'b'}, 2: {3: 'a', 4: 'b'}, 3: {4: 'a', 5: 'b'}, 11: {12: 'a', 13: 'b'}, 12: {13: 'a', 14: 'b'}}
Note the new keys and its new inner dictionaries (products)
The thing is that I'd like to keep doing this with new products in a while loop until a number is achieved.
For instance for the next iteration products will be:
products = [3,4,5,12,13,14]
They should be used to apply additions if they are not in D, so this can be easily done by:
for i in products:
if i in D:
products.remove(i)
which will lead us to:
products = [4,5,13,14] # 3 and 12 are already on D
So we should apply addition to these products and add them to D
So I guess that to achieve this there must be something like:
D = {1: {2: 'a', 3: 'b'}, 10: {11: 'a', 12: 'b'}}
i = 0
while i < 4: # just an example of 4 number of iterations
products = list(set([l for x, y in D.items() for l, m in y.items()]))
for j in products:
if j in D:
products.remove(j)
# apply additions
# update D or use an auxiliary dict and them append to D
i +=1
There's just a single variable i, there.
You'll want to use for j on the inner loop,
to avoid disturbing the outer loop.

Convert a pair of list objects into dictionary with duplicates included

I can club two lists into a dictionary as below -
list1 = [1,2,3,4]
list2 = ['a','b','c','d']
dct = dict(zip(list1, list2))
print(dct)
Result,
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}
However with duplicates as below,
list3 = [1,2,3,3,4,4]
list4 = ['a','b','c','d','e','f']
dct_ = dict(zip(list1, list2))
print(dct)
I get,
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}
What should i do to consider the duplicates in my list as individual keys in my resulting dictionary?
I am expecting results as below -
{1: 'a', 2: 'b', 3: 'c', 3: 'd', 4: 'e', 4: 'f'}
Instead you can create the dictionary with values as list:
from collections import defaultdict
d = defaultdict(list)
for k,v in zip(list3, list4):
d[k].append(v)
defaultdict(list, {1: ['a'], 2: ['b'], 3: ['c', 'd'], 4: ['e', 'f']})
You can't have duplicate keys in a dictionary. However, you can have multiple values(a list) mapped to each key.
An easy way to do this is with dict.setdefault():
list3 = [1,2,3,3,4,4]
list4 = ['a','b','c','d','e','f']
d = {}
for x, y in zip(list3, list4):
d.setdefault(x, []).append(y)
print(d)
# {1: ['a'], 2: ['b'], 3: ['c', 'd'], 4: ['e', 'f']}
The other option is to use a collections.defaultdict(), as shown in #YOLO's answer.

Permutation mapping of two lists in python

How can I create a permutation mapping of two lists in python?
For example I have two lists [1,2,3] and ['A','B','C']
Then my code should generate a list of 6 dictionaries
[ {1:'A',2:'B',3:'C'},
{1:'A',2:'C',3:'B'},
{1:'B',2:'A',3:'C'},
{1:'B',2:'C',3:'A'},
{1:'C',2:'A',3:'B'},
{1:'C',2:'B',3:'A'} ]
Using zip and itertools.permutations in a list comprehension:
>>> from itertools import permutations
>>> L1 = [1,2,3]
>>> L2 = ['A','B','C']
>>> [dict(zip(L1, p)) for p in permutations(L2)]
[{1: 'A', 2: 'B', 3: 'C'},
{1: 'A', 2: 'C', 3: 'B'},
{1: 'B', 2: 'A', 3: 'C'},
{1: 'B', 2: 'C', 3: 'A'},
{1: 'C', 2: 'A', 3: 'B'},
{1: 'C', 2: 'B', 3: 'A'}]
You seem to permutate only the values of the dicts, so you could do something like
from itertools import permutations
dicts = []
keys = [1, 2, 3]
for values in permutations(['A', 'B', 'C']):
new_dict = dict(zip(keys, values))
dicts.append(new_dict)

python dict remove duplicate values by key's value?

A dict
dic = {
1: 'a',
2: 'a',
3: 'b',
4: 'a',
5: 'c',
6: 'd',
7: 'd',
8: 'a',
9: 'a'}
I want to remove duplicate values just keep one K/V pair,
Regarding the "key" selection of those duplicated values, it may be max or min or by random select one of those duplicated item's key.
I do not want to use a k/v swap since that can not control the key selection.
Take value "a" for example
1: 'a',
2: 'a',
4: 'a',
8: 'a',
9: 'a'
the max key will be {9: 'a'} and the min will be {1: 'a'}, and the random will choise any one of it.
And, if the key is other kind of hashable value, for example, string, then how to do such a selection?
Can anyone share me an idea?
Thanks!
You could build a reverse dictionary where the values are lists of all the keys from your initial dictionary. Using this you could then do what you want, min, max, random, alternate min and max, or whatever.
from collections import defaultdict
d = defaultdict(list)
for k,v in dic.iteritems():
d[v].append(k)
print d
# {'a': [1, 2, 4, 8, 9], 'c': [5], 'b': [3], 'd': [6, 7]}
import itertools as it
newdic = {}
for v, grp in it.groupby(sorted((v, k) for k, v in dic.items)):
newdic[min(k for _, k in grp)] = v
Or other "selection" functions in lieu of min (which, of course, does work fine even if keys are strings -- will give you the "lexically first" key in that case).
The one case in which the selection function needs some care is when the keys corresponding to the same value may be non-comparable (e.g., complex numbers, or, in Python 3, objects of different not-all-numeric types). Nothing a key= in the min won't cure;-).
This will give you a randomly selected unique key:
In [29]: dic
Out[29]: {1: 'a', 2: 'a', 3: 'b', 4: 'a', 5: 'c', 6: 'd', 7: 'd', 8: 'a', 9: 'a'}
In [30]: dict((v,k) for k,v in dic.iteritems())
Out[30]: {'a': 9, 'b': 3, 'c': 5, 'd': 7}
In [31]: dict((v,k) for k,v in dict((v,k) for k,v in dic.iteritems()).iteritems())
Out[31]: {3: 'b', 5: 'c', 7: 'd', 9: 'a'}

Categories

Resources