Delete certain keys from a nested dictionary using dictionary comprehension - python

I have to work with a nested dictionary filled with numbers in python 'my_dict', and a list that specifies the subkeys 'keys 'that have to be deleted:
keys=[1,9]
my_dict={1:{1:a, 2:b}, 2:{1:c, 3:d, 9:e}}
I want to have two outcomes:
Delete all subkeys+their values if they are in keys, e.g.
new_dict={1:{2:b}, 2:{3:d}}
Or Delete all subkeys+their values if they are not in keys, e.g.
new_dict:{1:{1:a}, 2:{1:c,9:e}}
I have tried:
new_list = {outer_k: {inner_k: inner_v for inner_k, inner_v in outer_v.items()-{1,9}} for outer_k, outer_v in my_dict.items()}
It gives me back the same dict without deletion of the elements, same for the second szenario
new_list = {outer_k: {inner_k: inner_v for inner_k, inner_v in outer_v.items()&{1,9}} for outer_k, outer_v in my_dict.items()}
I have also tried:
for key, value in my_dict.items():
for key1, value1 in value.items():
for key1 in keys:
try:
del dict[key1]
except KeyError:
pass
This gives me the error:
TypeError: 'type' object does not support item deletion
I would be glad if anyone knows of a neat solution for this!

Using dict comprehension:
new_dict = {k_out : {k_in : v_in for k_in, v_in in v_out.items() if k_in in keys} for k_out,v_out in my_dict.items()}

The error TypeError: 'type' object does not support item deletion stems from
for key, value in my_dict.items():
for key1, value1 in value.items():
for key1 in keys:
try:
del dict[key1] # this is the build in, not your instance
# fix: del my_dict[key1]
except KeyError:
pass
As for the comprehensions - they look rather long ....
keys = [1,9]
my_dict = {1:{1:"a", 2:"b"}, 2:{1:"c", 3:"d", 9:"e"}}
# delete top levels
# for unwanted_key in keys:
# try:
# del my_dict[unwanted_key]
# except KeyError:
# pass
# delete inside sub levels
for unwanted_key in keys:
for inner_key in my_dict:
try:
del my_dict[inner_key][unwanted_key]
except KeyError:
pass
print( my_dict)
Results in
# {2: {3: 'd'}} # if you do top level and sub level
{1: {2: 'b'}, 2: {3: 'd'}} # if you only do sub levels
The try: ... except: ... follows "Ask forgiveness not permission" - explain

For your example , i recreated the data to runnable,
however deleting directly from the dict you' re iterating on will throw an error , you can do it by creating a copy of a dictionary where will your result will be stored
keys=[1,9]
my_dict={1:{1:"a", 2:"b"}, 2:{1:"c", 3:"d", 9:"e"}}
my_dict
new_dict1=my_dict.copy()
for key, value in my_dict.items():
if key1 in value.keys() and key1 in keys:
# delete subkeys and their sub values
del new_dict1[key][key1]

You can modify my_dict in situ like this:
keys = [1, 9]
my_dict = {1: {1: 'a', 2: 'b'}, 2: {1: 'c', 3: 'd', 9: 'e'}}
for v in my_dict.values():
for k in keys:
if k in v:
del v[k]
print(my_dict)
Output:
{1: {2: 'b'}, 2: {3: 'd'}}

Related

How to efficiently remove elements from dicts that have certain value patterns?

For example, in dict1 the keys 1, 2, 3 all have the same value 'a', but the keys 3 and 5 have different values, 'b' and 'd'. What I want is:
If N keys have the same value and N >=3, then I want to remove all other elements from the dict and only keep those N key values, which means 'b' & 'd' have to be removed from the dict.
The following code works, but it seems very verbose. Is there a better way to do this?
from collections import defaultdict
dict1 = {1:'a', 2:'a', '3':'b', '4': 'a', '5':'d'}
l1 = [1, 2, 3, 4, 5]
dict2 = defaultdict(list)
for k, v in dict1.items():
dict2[v].append(k)
to_be_removed = []
is_to_be_removed = False
for k, values in dict2.items():
majority = len(values)
if majority>=3:
is_to_be_removed = True
else:
to_be_removed.extend(values)
if is_to_be_removed:
for d in to_be_removed:
del dict1[d]
print(f'New dict: {dict1}')
You can use collections.Counter to get the frequency of every value, then use a dictionary comprehension to retain only the keys that have the desired corresponding value:
from collections import Counter
dict1 = {1:'a', 2:'a', '3':'b', '4': 'a', '5':'d'}
ctr = Counter(dict1.values())
result = {key: value for key, value in dict1.items() if ctr[value] >= 3}
print(result)
This outputs:
{1: 'a', 2: 'a', '4': 'a'}

Dictionary Comprehension in Python for key:[1,2,3] [duplicate]

This question already has answers here:
is it possible to reverse a dictionary in python using dictionary comprehension
(5 answers)
Closed 2 years ago.
While I've been improving my Python skills I have one question.
My code is below:
# def invertDictionary(dict):
# new_dict = {}
# for key, value in dict.items():
# if value in new_dict:
# new_dict[value].append(key)
# else:
# new_dict[value]=[key]
# return new_dict
def invertDictionary(dict):
new_dict = {value:([key] if value else [key]) for key, value in dict.items()}
return new_dict;
invertDictionary({'a':3, 'b':3, 'c':3})
I am trying to get output like {3:['a','b','c']}. I have achieved that using a normal for-loop; I just want to know how to get these results using a Dictionary Comprehension. I tried but in append it's getting an error. Please let me know how to achieve this.
Thanks in Advance!
You missed that you also need a list comprehension to build the list.
Iterate over the values in the dict, and build the needed list of keys for each one.
Note that this is a quadratic process, whereas the canonical (and more readable) for loop is linear.
d = {'a':3, 'b':3, 'c':3, 'e':4, 'f':4, 'g':0}
inv_dict = {v: [key for key, val in d.items() if val == v]
for v in set(d.values())}
result:
{0: ['g'],
3: ['a', 'b', 'c'],
4: ['e', 'f']
}
Will this do?
while your original version with a regular for loop is the best solution for this, here is a variation on #Prune answer that doesn't goes over the dict multiple times
>>> import itertools
>>> d = {'a':3, 'b':3, 'c':3, 'e':4, 'f':4, 'g':0}
>>> {group_key:[k for k,_ in dict_items]
for group_key,dict_items in itertools.groupby(
sorted(d.items(),key=lambda x:x[-1]),
key=lambda x:x[-1]
)
}
{0: ['g'], 3: ['a', 'b', 'c'], 4: ['e', 'f']}
>>>
first we sorted the items of the dict by value with a key function to sorted using a lambda function to extract the value part of the item tuple, then we use the groupby to group those with the same value together with the same key function and finally with a list comprehension extract just the key
--
as noted by Kelly, we can use the get method from the dict to get the value to make it shorter and use the fact that iteration over a dict give you its keys
>>> {k: list(g) for k, g in itertools.groupby(sorted(d, key=d.get), d.get)}
{0: ['g'], 3: ['a', 'b', 'c'], 4: ['e', 'f']}
>>>
You could use a defalutdict and the append method.
from collections import defaultdict
dict1 = {'a': 3, 'b': 3, 'c': 3}
dict2 = defaultdict(list)
{dict2[v].append(k) for k, v in dict1.items()}
dict2
>>> defaultdict(list, {3: ['a', 'b', 'c']})

Python Join 2 dictionaries into 3rd, 2D dictionary where key from dictionary 2 is list value in dictionary 1

I couldn't find this particular python dictionary question anywhere.
I have two dictionaries:
dict1 = {'key1':['val1','val2','val3']}
dict2 = {'val1':['a','b','c']}
I want a 3rd, 2D dictionary with:
dict3 = {'key1': {'val1':['a','b','c']} }
So, joining 2 dictionaries where the key of the second dictionary is a list value of the first dictionary.
I was trying some nested looping along the lines of:
for key1, val1 in dict1.items():
for key2, in val2 in dict2.items():
# do something here
I am not sure if that is the best way to do this.
This is best done by iterating over dict1 and looking for matching values in dict2:
result = {}
for key, value_list in dict1.items():
result[key] = subdict = {}
for value in value_list:
try:
subdict[value] = dict2[value]
except KeyError:
pass
Result:
{'key1': {'val1': ['a', 'b', 'c']}}
You can use a dictionary comprehension and then check if the final result contains only one dictionary. If the latter is true, then a dictionary of dictionaries will be the final result; else, a listing of dictionaries will be stored for the key:
dict1 = {'key1':['val1','val2','val3']}
dict2 = {'val1':['a','b','c']}
new_dict = {a:[{i:dict2[i]} for i in b if i in dict2] for a, b in dict1.items()}
last_result = {a:b if len(b) > 1 else b[0] for a, b in new_dict.items()}
Output:
{'key1': {'val1': ['a', 'b', 'c']}}
dict1 = {
'key1':['val1','val2','val3']
}
dict2 = {
'val1':['a','b','c']
}
dict3 = {
key : { val_key : dict2[val_key]
for val_key in val_list if (val_key in dict2.keys())
} for key, val_list in dict1.items()
}
You can try this solution .
dict1 = {'key1':['val1','val2','val3']}
dict2 = {'val1':['a','b','c']}
join_dict={}
for i,j in dict1.items():
for sub_l,sub_value in dict2.items():
if sub_l in j:
join_dict[i]={sub_l:sub_value}
print(join_dict)
output:
{'key1': {'val1': ['a', 'b', 'c']}}

In python, combine the values of 2 dictionaries

I'm new to Python, and I have two questions regarding dictionary in python.
I have dict1 (where the values is already a list), then I have dict2with the same keys as dict1, I want to add the new value to the list of values in dict1. How can I write it? Can you show it with an example?
dict1 = {'uid': ['u1'], 'sid': ['s1'], 'os': ['os1']}
dict2 = {'uid': ['u2'], 'sid': ['s2'], 'os': ['os2']}
Expected output:
dict1 = {'uid': ['u1', 'u2'], 'sid': ['s1', 's2'], 'os': ['os1', 'os2']}
I will be process a lot of lines in a text file, with each line creating a new dictionary. Here is the idea of my code:
count = 0
for line in f:
if count == 0:
dict1 = parse_qs(line)
count = count+1
else:
dict2 = parse_qs(line)
#combine dict1 with dict 2, and assign the new dict to dict1
Is there a better way that uses less memory or runs faster (still using dictionary)?
Thank you in advance for your help!
Since you're apparently not allowed to rewrite you parser, you can do:
for k in dict1:
dict1[k].extend(dict2.get(k, []))
You can drop the .get and use direct subscription if the keys from both dicts are always matching.
for k in dict1:
dict1[k].extend(dict2[k])
Otherwise, you can create one defaultdict(list) and let your parser append values to that.
You can do like this,
for key in dict1.keys():
if dict2.has_key(key):
dict1[key].extend(dict2[key])
if you are using python3 you can use key in dict2 instead of dict2.has_key(key).
Result
{'os': ['os1', 'os2'], 'sid': ['s1', 's2'], 'uid': ['u1', 'u2']}
If you don't want the two dictionaries anymore then you can do this:
def merge_dict(dict1, dict2):
for k in dict1:
try:
dict1[k].extend(dict2[k])
except KeyError:
pass
return dict1
Else if you want to preserve them both for future use, try this
def merge_dict(dict1, dict2):
new_dict = {}
keys = dict1.keys() + dict2.keys()
for k in keys:
try:
new_dict[k] = dict1[k]
except KeyError:
new_dict[k] = dict2[k]
continue
try:
new_dict[k]+= dict2[k]
except KeyError:
pass
return dict1
this may be helpful to you.
from itertools import chain
from collections import defaultdict
# ------ for dict1 -------#
u1 = [1,2,3]
s1 = ['a','b','c']
x1 = [10,100,1000]
# ------ for dict2 -------#
u2 = [4,5,6]
s2 = ['d','e','f']
x2 = ['aa','bb','cc']
dict1 = {1:u1,2:s1,3:x1}
dict2 = {1:u2,2:s2,3:x2}
dict3 = defaultdict(list)
for a, b in chain(dict1.items(), dict2.items()):
dict3[a].append(b)
#for a, b in dict3.items():
# print(a, b)
print dict3

How to remove dictionary key with known value?

Assume python dict:
mydict = {'a': 100, 'b': 200, 'c': 300}
I know one of the values:
value = 200
How to remove the 'b': 200 pair from the dict? I need this:
mydict = {'a': 100, 'c': 300}
Use a dictionary comprehension. Note that (as jonrsharpe has stated) this will create a new dictionary which excludes the key:value pair that you want to remove. If you want to delete it from your original dictionary then please see his answer.
>>> d = {'a': 100, 'b': 200, 'c': 300}
>>> val = 200
# Use d.items() for Python 2.x and d.iteritems() for Python 3.x
>>> d2 = {k:v for k,v in d.items() if v != val}
>>> d2
{'a': 100, 'c': 300}
It sounds like you want:
for key, val in list(mydict.items()):
if val == value:
del mydict[key]
break # unless you want to remove multiple occurences
You'll need to loop over every items(), either with dict comprehension:
new_dict = {k:v for k,v in my_dict.items() if predicate(value)}
Or modifying the existing dictionary:
for k,v in my_dict.items():
if not predicate(v):
del my_dict[k]
The simplest i found:
for key in [k for k,v in mydict.items() if v==200]:
del mydict[key]

Categories