# aDictionary = { <aKey>: <aValue>, ... } python dictionary constructor
aDictionary = { 'ida': '1', \
'idb': '2', \
'idc': '3', \
'idd': '3', \
'ide': '4', \
'idf': '4', \
'idg': '4' \
}
# the OP used aDictionaryOfDictionaries construction instead.
# aDictOfDICTs = {'ida':{'key':'1'},'idb':{'key':'2'}, ... }
# The algorithmisation of the processing task is different.
# for aDictionary, a inverseDictionary may be assembled
# to hold for each <aValue>,
# a referring <aKey>, incl. those, where are more than one primary <aKey>-s,
# ( stored asListOfPrimaryKEYs or aSetOfPrimaryKEYs ), which len() is easy to test
A fair & robust solution for a proper anInverseDICTIONARY construction
can be found at "Fast functional solution for non-bijective maps (values not unique):" in >>> https://stackoverflow.com/a/22235665/3666197
Beware, not all posts yield a robust solution there.
Finally
>>> aListOfDUP_KEYs = [ anInvDictKEY for anInvDictKEY in anInverseDICTIONARY.keys() if ( len( anInverseDICTIONARY[anInvDictKEY] ) > 1 ) ]
[ 3, 4 ]
''' as anInverseDICTIONARY = { 1: set( [ 'ida' ] ),
2: set( [ 'idb' ] ),
3: set( [ 'idd', 'idc' ] ),
4: set( [ 'idg', 'idf', 'ide' ] )
}
'''
Remove whichever DUP-e from anInverseDICTIONARY via aDUP_KEY, or from the original aDictionary
by a list comprehension
>>> [ aDictionary.pop( anInverseDICTIONARY[aDUP_KEY].pop() ) for aDUP_KEY in aListOfDUP_KEYs ]
[ 'idd', 'idf' ]