Remove duplicate value from dictionary without removing key - python

I'm new to Python and am having to learn by trial and error, but I haven't been able to find a solution to the problem I'm having.
I have a dictionary that looks something like this:
myDict = {'key1': ['item1', 'item2', 'item3'], 'key2': ['item4', 'item5', 'item6'],
'key3': 'item7', 'key4': 'item8', 'key5': ['item1', 'item2', 'item3'], 'key6': 'item7'}
I need to remove duplicate values from the dictionary and replace them with an empty value (""). Found a couple solution on here but they are working as intended
for key, value in myDict.items():
if values not in key newDict.values():
myDict[key] = value
else:
myDict[key] = ""
print newDict
This is removing all the values and is outputting
# newDict:{key1: '', key2: '', key3: '', key4: '', key5: '', key6: '')
I'm looking for the output to be
# newDict = {'key1': '', 'key2':['item4', 'item5', 'item6'], 'key3': '', 'key4':
'item8', key5: ['item1', 'item2', 'item3'], 'key6': 'item7'}

You have the right overall idea, but there are three problems with your code:
You're storing values back into myDict instead of into newDict.
On line 2, you're checking values instead of value.
Also on line 2, key shouldn't be there, and throws a SyntaxError.
Here is the correct code:
newDict = {}
for key, value in myDict.iteritems():
if value not in newDict.values():
newDict[key] = value
else:
newDict[key] = ""
print newDict
If you're not in the anti-ternary operator camp, you could also shorten it to this:
newDict = {}
for key, value in myDict.iteritems():
newDict[key] = value if value not in newDict.values() else ""
print newDict
Or, if you would rather just remove the values from the original dict (myDict) instead of building a new one (newDict), you could do this:
foundValues = []
for key, value in myDict.iteritems():
if value not in foundValues:
foundValues.append(myDict[key])
else:
myDict[key] = ""
print myDict
If you need duplicate values removed in a specific order, check out OrderedDicts.
Update:
In light of the updated requirements -- that values be removed from the original dict, starting from the beginning -- if you're able to simply initialize myDict with an OrderedDict instead of a dict, all you need to do is replace this:
myDict = {'key1': ['item1', 'item2', 'item3'], 'key2': ['item4', 'item5', 'item6'], 'key3': 'item7', 'key4': 'item8', 'key5': ['item1', 'item2', 'item3'], 'key6': 'item7'}
with this:
from collections import OrderedDict
…
myDict = OrderedDict([('key1', ['item1', 'item2', 'item3']), ('key2', ['item4', 'item5', 'item6']), ('key3', 'item7'), ('key4', 'item8'), ('key5', ['item1', 'item2', 'item3']), ('key6', 'item7')])
and then use the same code provided above.

This does it:
myDict_values = myDict.values() # better than calling this numerous times
for key in myDict.keys():
if myDict_values.count(myDict[key]) > 1: myDict[key] = ""
This won't guarantee that key5 will be blank instead of key1, because dictionaries are not ordered.

Related

Creating nested dictionary from two lists one of which contains dictionaries

Very similar to this question but with an added caveat.
I have two lists identical in length. One contains my keys, and the other contains a list of dictionaries belonging to said keys. Example below (yes the Nones are intentional)
keys = ['key1', 'key2, 'key3']
vals = [[{'subkey1': 'val1', 'subkey2': 'val2'}], [{'subkey1: 'val2'},None],[{'subkey1':'val3'}, {'subkey3':'val1}, None, None]]
What I'd like to do is match each dictionary in each list to a key to create a nested dict.
So something like below:
final = {'key1':{'subkey1': 'val1', 'subkey2': 'val2'}, 'key2':{'subkey1': 'val2'}, 'key3':{'subkey1':'val3'}, {'subkey3':'val1'}}
I know we can use dictionary comprehension to do this. I created a dictionary of empty dicts.
s = {}
for i in keys:
for v in vals:
s[i] = {}
break
Then I wrote the following to iterate through the list of dicts I'd like to pair up accounting for the non dictionary values in my list.
x = 0
while x < len(keys):
for i in keys:
for element in vals[x]:
if isinstance(element, dict) == True:
s[str(i)].append(element)
else:
print('no')
x=x+1
However when I run this, I get AttributeError: 'NoneType' object has no attribute 'append'
How can I append to a dictionary using this for loop?
For the less readable dictionary comprehension solution
keys = ['key1', 'key2', 'key3']
vals = [
[{'subkey1': 'val1', 'subkey2': 'val2'}],
[{'subkey1': 'val2'}, None],
[{'subkey1': 'val3'}, {'subkey3':'val1'}, None, None]
]
s = {
k: {
sk: sv for d in (x for x in v if x is not None) for sk, sv in d.items()
} for k, v in zip(keys, vals)
}
Gives
{'key1': {'subkey1': 'val1', 'subkey2': 'val2'},
'key2': {'subkey1': 'val2'},
'key3': {'subkey1': 'val3', 'subkey3': 'val1'}}
This can be done fairly easily using a defaultdict:
from collections import defaultdict
keys = ['key1', 'key2', 'key3']
vals = [
[{'subkey1': 'val1', 'subkey2': 'val2'}],
[{'subkey1': 'val2'},None],
[{'subkey1':'val3'}, {'subkey3':'val1'}, None, None]
]
final = defaultdict(dict)
for idx, key in enumerate(keys):
for d in vals[idx]:
if d is not None:
final[key].update(d)
Output:
defaultdict(<class 'dict'>, {
'key1': {'subkey1': 'val1', 'subkey2': 'val2'},
'key2': {'subkey1': 'val2'},
'key3': {'subkey1': 'val3', 'subkey3': 'val1'}
})
can't use append on a dict
try this maybe?
keys = ['key1', 'key2', 'key3']
vals = [[{'subkey1': 'val1', 'subkey2': 'val2'}], [{'subkey1': 'val2'},None],[{'subkey1':'val3'}, {'subkey3':'val1'}, None, None]]
s = {}
for i in keys:
s[i] = {}
for i, key in enumerate(keys):
for element in vals[i]:
if isinstance(element, dict) == True:
s[str(key)].update(element)
print(s)
Output:
{'key1': {'subkey1': 'val1', 'subkey2': 'val2'}, 'key2': {'subkey1': 'val2'}, 'key3': {'subkey1': 'val3', 'subkey3': 'val1'}}

How to iterate and change an item in-place in a list inside a dict

I have some lists as values keys in a dict. Is there some way to iterate through the dict to perform changes in-place on the lists?
dictio = {
'A' : ['item1', 'item2', 'item3'],
'B' : ['item4', 'item5', 'item3']
}
Supposing that I want to alter all the items on both keys:
if dictio[key] == 'item3'
//change all occurences in all lists...
I've tried to loop through the dict with a for loop and dict.items() but can't figure out how.
dictio = {
'A' : ['item1', 'item2', 'item3'],
'B' : ['item4', 'item5', 'item3']
}
for key, value in dictio.items():
dictio[key] = ['new_item3' if item == 'item3' else item for item in value]
print(dictio)
output
{'A': ['item1', 'item2', 'new_item3'], 'B': ['item4', 'item5', 'new_item3']}
You have to test if your value is in the list and change like shown below:
for k,v in dictio.items():
if 'item3' in v:
#change value here
v.pop(v.index('item3'))
v.append("new Item")
A combination of a loop and a list comprehension, like this:
dictio = {
'A' : ['item1', 'item2', 'item3'],
'B' : ['item4', 'item5', 'item3']
}
for key in dictio.keys():
dictio[key] = [ 'replaced' if x == 'item3' else x for x in dictio[key]]
print(dictio)
Outputs:
{'A': ['item1', 'item2', 'replaced'], 'B': ['item4', 'item5', 'replaced']}

Replace first dictionary value with value from second if blank

I would like dict3 to match dict2 after running it through this loop. It doesn't have to be a loop if there is an easier way.
dict1={'key1' : 'val1', 'key2' : ''}
dict2={'key1' : 'val1', 'key2' : 'val2'}
dict3=dict1
#pseudocode
for key in dict1.keys():
if value is not None:
#no need to do anything
else:
dict3[value] = dict2[value]
What I would like is for dict3 to contain keys and values matching dict2.
I believe you need a dict comprehension with .copy
Ex:
dict1 = {'key1' : 'val1', 'key2' : ''}
dict2 = {'key1' : 'val1', 'key2' : 'val2'}
dict3 = {k: v if v else dict2.get(k, v) for k, v in dict1.items() }
print(dict3) #--> {'key1': 'val1', 'key2': 'val2'}

Searching if the values on a list is in the dictionary whose format is key-string, value-list(strings)

my_dict = { # This dictionary is generated thru
'a' : [ 'value1', 'value4', 'value5' ], # the info given by the user
'b' : [ 'value2', 'value6', 'value7'],
'c' : [ 'value3', 'value8', 'value9']
}
list = [ 'value1', 'value2' ] # List is generated using list comprehension
I need to generate a list that will output something like this:
output_list = ['a', 'b']
I need to check if the values on "list" matches with the values on the list inside of the dictionary. Is this even possible?
I tried to use this but I only get an empty list:
[key for key, value in my_dict.items() if value in list]
You need to iterate over the list as well (and you should not be using list as a variable name, it shadows the built-in list function) . Example -
[key for item in lst for key,value in my_dict.items() if item in value]
Demo -
>>> my_dict = { # This dictionary is generated thru
... 'a' : [ 'value1', 'value4', 'value5' ], # the info given by the user
... 'b' : [ 'value2', 'value6', 'value7'],
... 'c' : [ 'value3', 'value8', 'value9']
... }
>>>
>>> lst = [ 'value1', 'value2' ]
>>> [key for item in lst for key,value in my_dict.items() if item in value]
['a', 'b']
You can get better performance, if you use set instead of list to store the values in the dictionary (since searching inside a set is O(1) operation, whereas searching inside a list is O(n) ). Example -
my_dict = {key:set(value) for key,value in my_dict.items()}
[key for item in lst for key,value in my_dict.items() if item in value]
Demo -
>>> my_dict = {key:set(value) for key,value in my_dict.items()}
>>> pprint(my_dict)
{'a': {'value4', 'value5', 'value1'},
'b': {'value6', 'value7', 'value2'},
'c': {'value3', 'value9', 'value8'}}
>>> lst = [ 'value1', 'value2' ]
>>> [key for item in lst for key,value in my_dict.items() if item in value]
['a', 'b']
If you are trying to check if any of the values in the list match with any value from the list in the dictionary, you can use set.intersection and check if the result is empty or not. Example -
[key for key, value in my_dict.items() if set(value).intersection(lst)]
This result will not be ordered, since dictionary does not have any specific order.
Demo -
>>> my_dict = {
... 'a' : [ 'value1', 'value4', 'value5' ],
... 'b' : [ 'value2', 'value6', 'value7'],
... 'c' : [ 'value3', 'value8', 'value9']
... }
>>> lst = [ 'value1', 'value2' ]
>>> [key for key, value in my_dict.items() if set(value).intersection(lst)]
['b', 'a']

How to check if all specifiedn keys exist in array?

For example I have something like this:
{'key1': 'value', 'key2': 'value', 'key3': 'value','key4': 'value','key4': 'value'}
And have an array:
['key1', 'key3']
How to check if all keys from second array exists in first?
You can use all and a generator expression. This will iterate over your array and check that each key is in the keys of d. If at least one of the keys is missing then it'll return False.
d = {'key1': 'value', 'key2': 'value', 'key3': 'value','key4': 'value','key4': 'value'}
a = ['key1', 'key3']
all(key in d for key in a) # True
a2 = ['key1', 'key5']
all(key in d for key in a2) # False
You can use set.issubset() method to check
a = {'key1': 'value', 'key2': 'value', 'key3': 'value','key4': 'value','key4': 'value'}
b = ['key1', 'key3']
set(b).issubset(a)
#True
c = ['key1', 'key5']
set(c).issubset(a)
#False
Edit: apply issubset directly on a according to #Maroun Maroun comment
Another way (using set and issubset):
set(['key1', 'key3']).issubset(your_dict)
Use a loop and in operator with dict object:
adict = {'key1': 'value', 'key2': 'value', 'key3': 'value','key4': 'value','key4': 'value'}
keys_list = ['key1', 'key3']
for key in keys_list:
if key in adict:
print key
"in" can check if the key in a dict

Categories