python dict remove duplicate values by key's value? - python

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'}

Related

Unpack values from nested dictionaries with depth level = 1

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'}

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

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)

Merging dictionaries using a counter

I have the following dictionaries (example):
>>> x = {'a': 'foo', 'b': 'foobar'}
>>> y = {'c': 'barfoo', 'd': 'bar'}
I want to take the keys of each and make them the value of another dict, say z, such that the keys of z is an incremented counter, equal to the length of both the dicts.
>>> z = {1: 'a', 2: 'b', 3: 'c', 4: 'd'}
As you can notice, the keys of z is an incremented counter and the values are the keys of x and y.
How do I achieve this? I have tried various solutions and playing with zip, but none seem to work. Probably because I have to update the z dict in succession.
Any suggestions?
In [1]: import itertools
In [2]: x = {'a': 'foo', 'b': 'foobar'}
In [3]: y = {'c': 'barfoo', 'd': 'bar'}
In [4]: z = [key for key in itertools.chain(x, y)]
In [5]: z
Out[5]: ['a', 'b', 'c', 'd']
In [6]: dict(enumerate(z))
Out[6]: {0: 'a', 1: 'b', 2: 'c', 3: 'd'}
In [7]: dict(enumerate(z, 1))
Out[7]: {1: 'a', 2: 'b', 3: 'c', 4: 'd'}
If you want duplicate keys to occur only once, replace [4] with this:
z = set(key for key in itertools.chain(x, y))
Note that you also could do everything at once (for this example I've added 'a': 'meow' to y):
In [15]: dict(enumerate(set(key for key in itertools.chain(x, y)), 1))
Out[15]: {1: 'a', 2: 'c', 3: 'b', 4: 'd'}
In [16]: dict(enumerate((key for key in itertools.chain(x, y)), 1))
Out[16]: {1: 'a', 2: 'b', 3: 'a', 4: 'c', 5: 'd'}
import itertools as it
{i+1:k for i,k in enumerate(it.chain(x,y))}
# {1: 'a', 2: 'b', 3: 'c', 4: 'd'}
Note that dict- (and related set-) comprehensions are new in v2.7+.

Categories

Resources