If you want to return the full nested dictionary if it contains a, a list comprehension is probably the cleanest way. Your initial list comprehension would work:
[foo[n] for n in foo if foo[n]['a'] == 10]
You can also avoid the lookup on n with:
[d for d in foo.values() if d['a'] == 10]
List comprehensions are generally considered more Pythonic than map or filter related approaches for simpler problems like this, though map and filter certainly have their place if you're using a pre-defined function.
This gives me the desired values but I'm not sure if iterating over values of a dictionary is a good/pythonic approach.
If you want to return all values of a dictionary that meet a certain criteria, I'm not sure how you would be able to get around not iterating over the values of a dictionary.
For a filter-based approach, I would do it as:
list(filter(lambda x: x['a'] == 10, foo.values()))
There's no need for the if-else in your original code.
If you want to return the full nested dictionary if it contains a, a list comprehension is probably the cleanest way. Your initial list comprehension would work:
[foo[n] for n in foo if foo[n]['a'] == 10]
You can also avoid the lookup on n with:
[d for d in foo.values() if d['a'] == 10]
List comprehensions are generally considered more Pythonic than map or filter related approaches for simpler problems like this, though map and filter certainly have their place if you're using a pre-defined function.
This gives me the desired values but I'm not sure if iterating over values of a dictionary is a good/pythonic approach.
If you want to return all values of a dictionary that meet a certain criteria, I'm not sure how you would be able to get around not iterating over the values of a dictionary.
For a filter-based approach, I would do it as:
list(filter(lambda x: x['a'] == 10, foo.values()))
There's no need for the if-else in your original code.
A list comprehension can do this beautifully.
>>> foo = {'m': {'a': 10}, 'n': {'a': 20}}
>>> [v for v in foo.values() if 10 in v.values()]
[{'a': 10}]
You don't need the for loop or the list comprehension if you are matching against a known key in the dictionary.
In [15]: if 10 in foo['m'].values():
...: result = [foo['m']]
...:
In [16]: result
Out[16]: [{'a': 10}]
I have a nested dictionary of source words, target words, and their frequency counts. It looks like this:
src_tgt_dict = {"each":{"chaque":3}, "in-front-of":{"devant":4}, "next-to":{"à-côté-de":5}, "for":{"pour":7}, "cauliflower":{"chou-fleur":4}, "on":{"sur":2, "panda-et":2}}I am trying to filter the dictionary so that only key-value pairs that are prepositions remain. To that end, I've written the following:
tgt_preps = set(["devant", "pour", "sur", "à"]) #set of target prepositions
src_tgt_dict = {"each":{"chaque":3}, "in-front-of":{"devant":4}, "next-to":{"à-côté-de":5}, "for":{"pour":7}, "cauliflower":{"chou-fleur":4}, "on":{"sur":2, "panda-et":2}}
new_tgt_preps = [] #list of new target prepositions
for src, d in src_tgt_dict.items(): #loop into the dictionary
for tgt, count in d.items(): #loop into the nested dictionary
check_prep = []
if "-" in tgt: #check to see if hyphen occurs in the target word (this is to capture multi-word prepositions that are not in the original preposition set)
check_prep.append(tgt[0:(tgt.index("-"))]) #if there's a hyphen, append the preceding word to the check_prep list
for t in check_prep:
if t in tgt_preps: # check to see if the token preceding the hyphen is a preposition
new_tgt_preps.append(tgt) #if yes, append the multi-word preposition to the list of new target prepositions
tgt_preps.update(new_tgt_preps) # update the set of prepositions to include the multi-word prepositions
temp_2_src_tgt_dict = {} # create new dict for filtering
for src, d in src_tgt_dict.items(): # loop into the dictionary
for tgt, count in d.items(): # loop into the nested dictionary
if tgt in tgt_preps: # if the target is in the set of target prepositions
temp_2_src_tgt_dict[tgt] = count # add to the new dict with the tgt as the key and the count as the valueWhen I print the new dict, I get the following:
{'devant': 4, 'pour': 7, 'sur': 2, 'à-côté-de': 5}
And it totally makes sense why I get that, because that's what I told the machine to do. But that's not my intention!
What I want is:
{"in-front-of:{"devant":4}, "for":{"pour":7}, "on":{"sur":2}, {"next-to":{"à-côté-de":5}}
I've tried to instantiate the nested dictionary by writing:
temp_2_src_tgt_dict[tgt][src] = count
but that throws up a Key Error.
Can anyone provide any suggestions or advice? Thank you in advance for your help.
python - Filtering nested dictionaries - Stack Overflow
Filter nested dictionary in Python
Filter nested dictionary in python based on key values - Stack Overflow
Filtering a nested (default) dict based on values
Videos
You can just use a dictionary comprehension over the Filme.items():
In [2]: {k:v for k,v in Filme.items() if v['Wertung'] >= 7}
Out[2]:
{'2': {'Jahr': 3,
'Preis': 70,
'Schauspielern': [''],
'Titel': 30,
'Wertung': 7},
'4': {'Jahr': 3,
'Preis': 20,
'Schauspielern': [''],
'Titel': 5,
'Wertung': 9},
'5': {'Jahr': 3,
'Preis': 100,
'Schauspielern': [''],
'Titel': 8,
'Wertung': 10},
'7': {'Jahr': 3,
'Preis': 20,
'Schauspielern': [''],
'Titel': 16,
'Wertung': 9},
'9': {'Jahr': 3,
'Preis': 20,
'Schauspielern': [''],
'Titel': 90,
'Wertung': 9}}
This is equivalent to the following:
new_dict = {}
for k,v in Filme.items():
if v['Wertung'] >= 7:
new_dict[k] = v
Dictionary comprehension will be most straight-forward.
>>> {i:j for i,j in Filme.items() if j['Wertung'] >=7}
{'7': {'Titel': 16, 'Wertung': 9, 'Preis': 20, 'Jahr': 3, 'Schauspielern': ['']}, '5': {'Titel': 8, 'Wertung': 10, 'Preis': 100, 'Jahr': 3, 'Schauspielern': ['']}, '4': {'Titel': 5, 'Wertung': 9, 'Preis': 20, 'Jahr': 3, 'Schauspielern': ['']}, '2': {'Titel': 30, 'Wertung': 7, 'Preis': 70, 'Jahr': 3, 'Schauspielern': ['']}, '9': {'Titel': 90, 'Wertung': 9, 'Preis': 20, 'Jahr': 3, 'Schauspielern': ['']}}
If there is a chance that Wertung might be absent for any entry, you can use get() to prevent the code from failing unexpectedly.
>>> {i:j for i,j in Filme.items() if j.get('Wertung',0) >=7}
You can define this recursively pretty easily with a dict comprehension.
def remove_keys_with_none_values(item):
if not hasattr(item, 'items'):
return item
else:
return {key: remove_keys_with_none_values(value) for key, value in item.items() if value is not None}
Recursion isn't too optimised in Python, but given the relatively small number of nestings that are likely, I wouldn't worry.
Looking before we leap isn't too Pythonic, I think it is a better option than catching the exception - as it's likely that the value will not be a dict most of the time (it is likely we have more leaves than branches).
Also note that in Python 2.x, you probably want to swap in iteritems() for items().
I really appreciate the answer by @Lattyware. It helped me filter out a nested object and remove empty values regardless of type being dict, list, or str.
Here is what I came up with:
remove-keys-with-empty-values.py
# remove-keys-with-empty-values.py
from pprint import pprint
def remove_keys_with_empty_values(item):
if hasattr(item, 'items'):
return {key: remove_keys_with_empty_values(value) for key, value in item.items() if value==0 or value}
elif isinstance(item, list):
return [remove_keys_with_empty_values(value) for value in item if value==0 or value]
else:
return item
d = {
'string': 'value',
'integer': 10,
'float': 0.5,
'zero': 0,
'empty_list': [],
'empty_dict': {},
'empty_string': '',
'none': None,
}
d['nested_dict'] = d.copy()
l = d.values()
d['nested_list'] = l
pprint({
"DICT FILTERED": remove_keys_with_empty_values(d),
"DICT ORIGINAL": d,
"LIST FILTERED": remove_keys_with_empty_values(l),
"LIST ORIGINAL": l,
})
execution
python remove-keys-with-empty-values.py
{'DICT FILTERED': {'float': 0.5,
'integer': 10,
'nested_dict': {'float': 0.5,
'integer': 10,
'string': 'value',
'zero': 0},
'nested_list': [0,
'value',
10,
0.5,
{'float': 0.5,
'integer': 10,
'string': 'value',
'zero': 0}],
'string': 'value',
'zero': 0},
'DICT ORIGINAL': {'empty_dict': {},
'empty_list': [],
'empty_string': '',
'float': 0.5,
'integer': 10,
'nested_dict': {'empty_dict': {},
'empty_list': [],
'empty_string': '',
'float': 0.5,
'integer': 10,
'none': None,
'string': 'value',
'zero': 0},
'nested_list': [{},
0,
'value',
None,
[],
10,
0.5,
'',
{'empty_dict': {},
'empty_list': [],
'empty_string': '',
'float': 0.5,
'integer': 10,
'none': None,
'string': 'value',
'zero': 0}],
'none': None,
'string': 'value',
'zero': 0},
'LIST FILTERED': [0,
'value',
10,
0.5,
{'float': 0.5,
'integer': 10,
'string': 'value',
'zero': 0}],
'LIST ORIGINAL': [{},
0,
'value',
None,
[],
10,
0.5,
'',
{'empty_dict': {},
'empty_list': [],
'empty_string': '',
'float': 0.5,
'integer': 10,
'none': None,
'string': 'value',
'zero': 0}]}
Hello.
I've gotten some help from redditors on a very similar problem before, but I've modified the problem slightly.
I have a nested defaultdict, the structure of which is {'source_word_string':{'target_word_string':frequency_int}}. When (pretty) printed, it looks like this:
defaultdict(<function <lambda> at 0x10948ee50>,
{'around': defaultdict(<class 'int'>, {'autour-de': 1}),
'from': defaultdict(<class 'int'>, {'en': 1}),
'in-front-of': defaultdict(<class 'int'>, {'devant': 1}),
'on': defaultdict(<class 'int'>, {'sur': 2, 'au-bord-d’': 1}),
'to': defaultdict(<class 'int'>, {'en': 1, 'à':3}),
'under': defaultdict(<class 'int'>, {'sous': 1})})I would like to delete all source-target word pairs whose frequency count is less than 2.
I would thus expect as output:
defaultdict(<function <lambda> at 0x108c57e50>,
{'on': defaultdict(<class 'int'>, {'sur': 2}),
{'to': defaultdict(<class 'int'>, {'à': 3})}) I think I need to do two nested loops (over source words and over target words) and then write to a new filtered_dict, but I'm not sure what that looks like in practice.
Any help would be greatly appreciated. Thank you in advance.
» pip install sndict
Maybe something like this:
from collections import defaultdict
new_tgt_dict = defaultdict(dict)
for k, v in src_tgt_dict.items():
for k1, v1 in v.items():
k_temp = k1
if "-" in k1:
k_temp = k1[0:(k1.index("-"))]
if k_temp in tgt_preps:
new_tgt_dict[k].update({k1: v1})
print(dict(new_tgt_dict))
{'in-front-of': {'devant': 4}, 'next-to': {'à-côté-de': 5}, 'for': {'pour': 7}, 'on': {'sur': 2}}
You could use a NestedDict. First install ndicts
pip install ndicts
Then
from ndicts.ndicts import NestedDict
tgt_preps = set(["devant", "pour", "sur", "à", "à-côté-de"]) # I added "à-côté-de"
src_tgt_dict = {
"each": {"chaque": 3},
"in-front-of": {"devant":4},
"next-to": {"à-côté-de": 5},
"for": {"pour": 7},
"cauliflower": {"chou-fleur": 4},
"on": {"sur":2, "panda-et":2}
}
for key, value in nd.copy().items():
if not set(key) & tgt_preps:
nd.pop(key)
If you need a dictionary as a result
result = nd.to_dict()
There are a few simple optimization you can try:
- make
comparison_listasetso the lookup is O(1) instead of O(n) - make
list_of_tuplesa generator, so you don't have to materialize all the entries at once - you can also integrate the condition into the generator itself
Example:
comparison_set = set(['value_1', 'value_2'])
tuples_generator = ((c['key'], x['value'])
for c in list_of_dict for x in c['data']
if x['value'] in comparison_set)
print(*tuples_generator)
# ('key1', 'value_1') ('key1', 'value_2')
Of course, you can also keep the comparison separate from the generator:
tuples_generator = ((c['key'], x['value'])
for c in list_of_dict for x in c['data'])
for k, v in tuples_generator:
if v in comparison_set:
print(k, v)
Or you could instead create a dict mapping values from comparison_set to keys from list_of_dicts. This will make finding the key to a particular value faster, but note that you can then only keep one key to each value.
values_dict = {x['value']: c['key']
for c in list_of_dict for x in c['data']
if x['value'] in comparison_set}
print(values_dict)
# {'value_2': 'key1', 'value_1': 'key1'}
In last step you can use filter something like this instead of iterating over that:
comparison_list = ['value_1', 'value_2']
print(list(filter(lambda x:x[1] in comparison_list,list_of_tuples)))
output:
[('key1', 'value_1'), ('key1', 'value_2')]