I have dictionary like below
dict1 = {'a':{'a':20, 'b':30}, 'b':{'a':30, 'b':40}, 'c':{'a':20, 'b':30}, 'd':{'a':30, 'b':40}}
Then in following dictionary two dictionaries are same, so expected result will be like below
result = [['a','c'],['b','d']]
>>> seen = {}
>>> dict1 = {'a':{'a':20, 'b':30}, 'b':{'a':30, 'b':40}, 'c':{'a':20, 'b':30}, 'd':{'a':30, 'b':40}}
>>> for k in dict1:
fs = frozenset(dict1[k].items())
seen.setdefault(fs, []).append(k)
>>> seen.values() # note: unordered
[['a', 'c'], ['b', 'd']]
If order is needed:
>>> from collections import OrderedDict
>>> dict1 = {'a':{'a':20, 'b':30}, 'b':{'a':30, 'b':40}, 'c':{'a':20, 'b':30}, 'd':{'a':30, 'b':40}}
>>> seen = OrderedDict()
>>> for k in sorted(dict1):
fs = frozenset(dict1[k].items())
seen.setdefault(fs, []).append(k)
>>> seen.values()
[['a', 'c'], ['b', 'd']]
Note: This code is currently cross-compatible on Python 2/3. On Python 2 you can make it more efficient by using .iteritems() instead of .items()
A quick one: 1st get different values, then list comprehension.
>>> values = []
>>> for k in dict1:
if dict1[k] not in values:
values.append(dict1[k])
>>> values
[{'a': 20, 'b': 30}, {'a': 30, 'b': 40}]
>>> [[k for k in dict1 if dict1[k] == v] for v in values]
[['a', 'c'], ['b', 'd']]
Related
I need to combine two dictionaries by their value, resulting in a new key which is the list of keys with the shared value. All I can find online is how to add two values with the same key or how to simply combine two dictionaries, so perhaps I am just searching in the wrong places.
To give an idea:
dic1 = {'A': 'B', 'C': 'D'}
dic2 = {'D': 'B', 'E': 'F'}
Should result in:
dic3 = {['A', 'D']: 'B', 'C': 'D', 'E': 'F'}
I am not sure why you would need such a data structure, you can probably find a better solution to your problem. However, just for the sake of answering your question, here is a possible solution:
dic1 = {'A':'B', 'C':'D'}
dic2 = {'D':'B', 'E':'F'}
key_list = list(dic2.keys())
val_list = list(dic2.values())
r = {}
for k,v in dic1.items():
if v in val_list:
i = val_list.index(v) #get index at value
k2 = key_list[i] #use index to retrive the key at value
r[(k, k2)] = v #make the dict entry
else:
r[k] = v
val_list = list(r.values()) #get all the values already processed
for k,v in dic2.items():
if v not in val_list: #if missing value
r[k] = v #add new entry
print(r)
output:
{('A', 'D'): 'B', 'C': 'D', 'E': 'F'}
You can't assign a list as a key in a python dictionary since the key must be hashable and a list is not an ashable object, so I have used a tuple instead.
I would use a defaultdict of lists and build a reversed dict and in the end reverse it while converting the lists to tuples (because lists are not hashable and can't be used as dict keys):
from collections import defaultdict
dic1 = {'A':'B', 'C':'D'}
dic2 = {'D':'B', 'E':'F'}
temp = defaultdict(list)
for d in (dic1, dic2):
for key, value in d.items():
temp[value].append(key)
print(temp)
res = {}
for key, value in temp.items():
if len(value) == 1:
res[value[0]] = key
else:
res[tuple(value)] = key
print(res)
The printout from this (showing the middle step of temp) is:
defaultdict(<class 'list'>, {'B': ['A', 'D'], 'D': ['C'], 'F': ['E']})
{('A', 'D'): 'B', 'C': 'D', 'E': 'F'}
If you are willing to compromise from 1-element tuples as keys, the second part will become much simpler:
res = {tuple(value): key for key, value in temp.items()}
I made a mistake in my question here (wrong requested input and expected output):
Comparing dicts, updating NOT overwriting values
I am not looking for this solution:
Combining 2 dictionaries with common key
So this question is not a duplicate
Problem statement:
requested input:
d1 = {'a': ['a'], 'b': ['b', 'c']}
d2 = {'b': ['c', 'd'], 'c': ['e','f']}
expected output (I don't care about the order of the keys / values!):
new_dict = {'a': ['a'], 'b': ['b', 'c', 'd'], 'c': ['e', 'f']}
The solution in Combining 2 dictionaries with common key
gives following output:
new_dict = {'a': ['a'], 'b': ['b', 'c', 'c', 'd'], 'c': ['e', 'f']}
I don't want the duplicates to be stored.
My solution (it works but it is not so efficient):
unique_vals = []
new_dict = {}
for key in list(d1.keys())+list(d2.keys()) :
unique_vals = []
try:
for val in d1[key]:
try:
for val1 in d2[key]:
if(val1 == val) and (val1 not in unique_vals):
unique_vals.append(val)
except:
continue
except:
new_dict[key] = unique_vals
new_dict[key] = unique_vals
for key in d1.keys():
for val in d1[key]:
if val not in new_dict[key]:
new_dict[key].append(val)
for key in d2.keys():
for val in d2[key]:
if val not in new_dict[key]:
new_dict[key].append(val)
Here is how I would go about it:
d1 = {'a': ['a'], 'b': ['b', 'c']}
d2 = {'b': ['c', 'd'], 'c': ['e','f']}
dd1 = {**d1, **d2}
dd2 = {**d2, **d1}
{k:list(set(dd1[k]).union(set(dd2[k]))) for k in dd1}
Produces the desired result.
I suggest using a default dictionary collection with a set as a default value.
It guarantees that all values will be unique and makes the code cleaner.
Talking about efficiecy it's O(n^2) by time.
from collections import defaultdict
d1 = {'a': ['a'], 'b': ['b', 'c']}
d2 = {'b': ['c', 'd'], 'c': ['e','f']}
new_dict = defaultdict(set)
for k, v in d1.items():
new_dict[k] = new_dict[k].union(set(v))
for k, v in d2.items():
new_dict[k] = new_dict[k].union(set(v))
Try this code. You can remove deep copy if modifications in the initial array are fine for you.
import copy
def merge(left, right):
res = copy.deepcopy(left)
for k, v in right.items():
res[k] = list(set(res[k]).union(v)) if k in res else v
return res
Simple if statement if you don't want to use a Set.
d3 = dict(d2)
for k,v in d1.items():
if k not in d3:
d3[k] = v
else:
for n in d1[k]:
if n not in d3[k]:
d3[k].append(n)
I need to make sure I don't have more than one of the same keys, if so, leave the first one and add their value (make it a list) to the existing key
this is what I tried:
my_dict = {1: "A", 2: "B", 1: "C"}
new_dict={}
list_keys = list(my_dict.keys())
list_values = list(my_dict.values())
for i in range(len(list_values)):
if list_keys[i] in new_dict.keys():
new_dict[list_keys[i]].append(list(list_values[i]))
else:
new_dict.update({list_keys[i]: list_values[i]})
return new_dict
The result required:
{1: ["A", "C"], 2: ["B"]}
The most concise way of reversing a dict like that uses a defaultdict:
from collections import defaultdict
d = {"A": 1, "B": 2, "C": 1}
rev = defaultdict(list)
for k, v in d.items():
rev[v].append(k)
rev
# defaultdict(<class 'list'>, {1: ['A', 'C'], 2: ['B']})
That first line doesn't make sense. A dictionary cannot have two values for the same key, so when you run that first line, the resulting dictionary is:
my_dict = {1: 'A', 2: 'B', 1: 'C'}
print(my_dict)
# {1: 'C', 2: 'B'}
What you could do is iterate over lists of desired keys and values, and build the dictionary that you want that way:
my_keys = [1, 2, 1]
my_vals = ['A', 'B', 'C']
my_dict = {}
for k, v in zip(my_keys, my_vals):
if k in my_dict.keys():
if not isinstance(my_dict[k], list):
my_dict[k] = [my_dict[k]] # convert to a list
my_dict[k].append(v)
else:
my_dict[k] = v
print(my_dict)
# {1: ['A', 'C'], 2: 'B'}
Based on the comments, you originally had a dictionary in_dict = {'A': 1, 'B': 2, 'C':1}. Given this in_dict, you can get the desired result by setting my_keys = in_dict.values() and my_vals = in_dict.keys() in the code above.
Using comprehensions if you want
a = {"A":1, "B":2, "C":1}
{value: [item_[0] for item_ in a.items() if item_[1] == value] for value in set(a.values())}
Output
{1: ['A', 'C'], 2: ['B']}
In Python 2.7: I'm measuring a process that counts the keys of a dictionary returned from a function.
A basic example is shown where the function getList() returns a list of chars which may be ['a'], ['b'], ['c'] or ['d']; most lists are single elements though two may be returned sometimes, e.g. ['a', 'd']. I'd like to count everything returned. A way I thought of doing this is shown below:
myDict = {'a':0, 'b':0, 'c':0, 'd':0, 'error':0, 'total':0}
for key in charList:
myDict[key] += 1
myDict['total'] += 1
Is there a more Pythonic way, perhaps dictionary comprehension to count keys within lists (of varying length)?
import random
def getList():
'''mimics a prcoess that returns a list of chars between a - d
[most lists are single elements, though some are two elements]'''
number = (random.randint(97,101))
if number == 101:
charList = [chr(number-1), chr(random.randint(97,100))]
if charList[0] == charList[1]:
getList()
else:
charList = [chr(number)]
return charList
myDict = {'a':0, 'b':0, 'c':0, 'd':0, 'error':0, 'total':0}
for counter in range(0,5):
charList = getList()
for key in charList:
print charList, '\t', key
try:
myDict[key] += 1
myDict['total'] += 1
except:
myDict['error'] += 1
print "\n",myDict
Output generated:
You can use the built-in collections.Counter class: https://docs.python.org/2/library/collections.html#collections.Counter
For example with your code:
import collections
ctr = collections.Counter()
for ii in range(0,5):
charList = getList()
ctr.update(charList)
ctr['total'] = sum(ctr.values())
print ctr
This will print:
Counter({'total': 7, 'd': 5, 'a': 1, 'c': 1})
You can use collections.Counter:
# You need to initialize the counter or you won't get the entry with 0 count.
myDict = collections.Counter({'a': 0, 'b': 0, 'c': 0, 'd': 0})
myDict.update(x for _ in range(0, 5) for x in getList())
# Then create the 'total' entry
myDict['total'] = sum(myDict.values())
Note: This may add new keys to the counter without setting the 'error' entry if the list returned by getList() contains new characters ('e', 'f', ...).
Use collections.Counter and a double-loop generator expression for feeding the individual elements into the counter:
>>> lst = [['a'], ['a', 'b'], ['c'], ['c', 'd']]
>>> c = collections.Counter((y for x in lst for y in x))
>>> c
Counter({'a': 2, 'c': 2, 'b': 1, 'd': 1})
>>> c.most_common(2)
[('a', 2), ('c', 2)]
>>> sum(c.values())
6
The easiest way I can think of is to flatten your list with chain and then use a Counter: let lst be the list [['a'], ['b'], ['c'], ['d'], ['a', 'd']]
>>> from itertools import chain
>>> from collections import Counter
>>> c = Counter(chain(*lst))
>>> c['total'] = sum(c.values())
>>> c
Counter({'total': 6, 'd': 2, 'a': 2, 'b': 1, 'c': 1})
#some dictionary data{}
data = {1:[3,1,4,2,6]}
How to print the key of data i.e
print( key_of(data) ) #print in some ways.
output:
1
What I got till now is to use data.keys() function but whenever I use that function output will be like:
dict_keys([1])
So, what is the problem with that function. Can any one suggest me the answer.?
Simply do:
print(list(data))
to print all the keys. If you want to print a specific key then you'll have to search for it:
print(next(key for key, value in data.items() if value == something))
Note that there is no guarantee on the order of the keys.
>>> data = {'a': 1, 'aa': 2, 'b': 3, 'cd': 4}
>>> list(data)
['cd', 'a', 'b', 'aa']
>>> data['f'] = 1
>>> list(data)
['cd', 'a', 'b', 'aa', 'f']
>>> del data['a']
>>> list(data)
['cd', 'b', 'aa', 'f']
>>> data[1] = 1
>>> list(data)
['cd', 1, 'b', 'aa', 'f']
>>> for c in ('cde', 'fgh', 'ijk', 'rmn'):
... data[c] = 1
...
>>> list(data)
[1, 'aa', 'cde', 'cd', 'rmn', 'b', 'ijk', 'fgh', 'f']
Note how the keys do not follow alphabetical order, and how inserted keys aren't always appended to the list of keys. Also after some insertions some key were swapped. Removing/inserting keys can change the order in unpredictable ways.
There's no issue with dict.keys, it returns a view like object in Python3. You can use list() on the dict to get a list of keys.
>>> data = {1:[3,1,4,2,6]}
>>> keys = list(data)
>>> keys
[1]
>>> keys[0]
1
Note: If dict contains more than one keys then the order can be arbitrary and keys[0] can return any key.
>>> dic = {'a12': 1, 'sd':'sdfd', 'sdfsd': 'sdfsd'}
>>> list(dic) #Arbitrarily ordered
['sdfsd', 'sd', 'a12']