Reputation: 380
I have two dictionaries:
mems = {'member2': ['PCP1', 'PCP2'],
'member6': ['PCP1', 'PCP5'],
'member7': ['PCP2', 'PCP4', 'PCP5'],
'member9': ['PCP1', 'PCP5']}
provs = {'PCP1': 2, 'PCP2': 1, 'PCP4': 1, 'PCP5': 1}
How do I check if an item in these list values is unique and doesnt appear anywhere else in the dictionary? If it is unique append the 'member' and that 'PCP' to a new dictionary called matches = dict()
remove that member from 'mems'
and then add any other leftover 'PCP' to their count in provs
. Oh, and remove the one that is unique in mems
result should look like
mems = {'member2': ['PCP1', 'PCP2'],
'member6': ['PCP1', 'PCP5'],
'member7': ['PCP2', 'PCP4', 'PCP5'],
'member9': ['PCP1', 'PCP5']}
provs = {'PCP1': 2, 'PCP2': 2, 'PCP5': 2}
matches = ['member7' : 'PCP4'}
I have no idea how to do this. I tried starting making a counter for the PCPs in mems
and finding the ones with 1 and then adding to matches
but it gives the wrong member, 'member9'.
pcpCounts = dict(collections.Counter(itertools.chain.from_iterable(new_members.values())))
print(pcpCounts)
{'PCP1': 3, 'PCP2': 2, 'PCP4': 1, 'PCP5': 2}
for (k,v), (k2,v2) in zip(memsCounter.items(), pcpCounter.items()):
if v2 == 1:
match[k] = k2
print(matches)
{'member9': 'PCP4'}
Why does the integrity not maintain when I do this. More importantly, is there a better way to do it?
Upvotes: 0
Views: 84
Reputation: 3907
To start it might make it easier if you create a set to see if it exists first, and then if it does, find the members it belongs to.
mems = {'member2': ['PCP1', 'PCP2'],
'member6': ['PCP1', 'PCP5'],
'member7': ['PCP2', 'PCP4', 'PCP5'],
'member9': ['PCP1', 'PCP5']}
provs = {'PCP1': 2, 'PCP2': 1, 'PCP4': 1, 'PCP5': 1}
members = {}
x = set()
for k,v in mems.items():
for i in v:
x.add(i)
try:
members[i].append(k)
except KeyError:
members[i] = [k,]
print(x)
print(members)
for k in provs:
if k in x:
print('KEY: {0} in {1}'.format(k,members[k]))
print('KEY: {0} in {1}'.format(k,len(members[k]))) # count the references.
Upvotes: 2
Reputation: 8180
mems = {'member2': ['PCP1', 'PCP2'],
'member6': ['PCP1', 'PCP5'],
'member7': ['PCP2', 'PCP4', 'PCP5'],
'member9': ['PCP1', 'PCP5']}
provs = {'PCP1': 2, 'PCP2': 1, 'PCP4': 1, 'PCP5': 1}
First, you can reverse mems
:
d1 = {}
for mem, pcps in mems.items():
for pcp in pcps:
d1.setdefault(pcp, []).append(mem)
# {'PCP1': ['member2', 'member6', 'member9'], 'PCP2': ['member2', 'member7'], 'PCP5': ['member6', 'member7', 'member9'], 'PCP4': ['member7']}
It's now easy to compute the matches
: filter the dict on len(mems)
and reverse it again:
matches = {mems[0]: pcp for pcp, mems in d1.items() if len(mems) == 1}
# {'member7': 'PCP4'}
You can create a new_mems
dict that does not contain the matching pcp
:
new_mems = {mem: [pcp for pcp in pcps if pcp not in matches.values()] for mem, pcps in mems.items()}
# {'member2': ['PCP1', 'PCP2'], 'member6': ['PCP1', 'PCP5'], 'member7': ['PCP2', 'PCP5'], 'member9': ['PCP1', 'PCP5']}
And then use a counter to update provs
:
import collections
new_provs = collections.Counter(provs)
for mem, pcp in matches.items():
new_provs.update(mems[mem]) # add 1 to each count
new_provs[pcp] -= 2 # was 0 + 1 (line above) -> is 0
# Counter({'PCP1': 2, 'PCP2': 2, 'PCP5': 2, 'PCP4': 0})
Upvotes: 1
Reputation: 27485
You can still use the Counter here just turn it into a set filtered by only the single occurring provs then use set.intersection
as your check.
from collections import Counter
mems = {'member2': ['PCP1', 'PCP2'], 'member6': ['PCP1', 'PCP5'], 'member7': ['PCP2', 'PCP4', 'PCP5'], 'member9': ['PCP1', 'PCP5']}
provs = {'PCP1': 2, 'PCP2': 1, 'PCP4': 1, 'PCP5': 1}
#I'm using sum here instead of itertools.chain. It serves the same purpose
prov_counts = {p for p, c in Counter(sum(mems.values(), [])).items() if c == 1}
prov_filter = map(prov_counts.intersection, mems.values())
matches = {m: list(p) for m, p in zip(mems, prov_filter) if p}
print(matches)
{'member7': ['PCP4']}
Upvotes: 2
Reputation: 373
One way of doing that is changing the second part of your code. Once you have the counts, you can iterate over original values checking which value is unique.
>>> mems = {'member2': ['PCP1', 'PCP2'],
... 'member6': ['PCP1', 'PCP5'],
... 'member7': ['PCP2', 'PCP4', 'PCP5'],
... 'member9': ['PCP1', 'PCP5']}
>>> import collections, itertools
>>> pcpCounts = collections.Counter(itertools.chain.from_iterable(mems.values()))
>>> matches = {}
>>> for k, pcps in mems.items():
... for v in pcps:
... if pcpCounts[v] == 1:
... matches[k] = v
...
>>> matches
{'member7': 'PCP4'}
Upvotes: 1