How to know if a value is inside a nested dictionary? - python

I'm new to Python and I'm still learning how to use. I have the follow dictionary:
dic = {'0': {'text': 'a', 'lang': 'es', 'rating': '4'}, '1': {'text': 'b', 'lang': 'es', 'rating': '3'}, '2': {'text': 'c', 'lang': 'es', 'rating': '1'}, '3': {'text': 'd', 'lang': 'es', 'rating': '2'}, '4': {'text': 'e', 'lang': 'es', 'rating': '5'}}
Now, I'm trying to know if the text, for example, 'a' is a value of any of those nested dictionaries (I know there is a value() function which returns the values of the dictionaries, but in this case it will only return the values of the first dictionary, won't it? Like 0,1,2,3,4)
I tried
for i in range(len(dic)):
if text in dic[i].values():
print("Yes")
else:
print("No")
But this gives me a KeyError with value '0'. I have searched for similar questions, but haven't found any which I can use to solve my problem. Can you please help me? Thanks in advance.

You can use any:
dic = {'0': {'text': 'a', 'lang': 'es', 'rating': '4'}, '1': {'text': 'b', 'lang': 'es', 'rating': '3'}, '2': {'text': 'c', 'lang': 'es', 'rating': '1'}, '3': {'text': 'd', 'lang': 'es', 'rating': '2'}, '4': {'text': 'e', 'lang': 'es', 'rating': '5'}}
result = any('a' in d.values() for d in dic.values())

You can use any with a generator comprehension, as suggested by #Ajax1234.
Or, if you do this repeatedly, you can calculate a set of values and check for membership with O(1) complexity. itertool.chain is a useful tool to combine nested values:
from itertools import chain
values = set(chain.from_iterable(i.values() for i in dic.values()))
res = 'a' in values # True

Starting first from what you have tried, you could indeed:
Use a for loop to iterate over the values of dic, that are dictionaries too.
Check inside each dictionary if the value of the key 'text' equals 'a'.
If yes, then print 'Yes' and stop the loop.
If the loop ends without any match, then print 'No'.
Let's do it with a beautiful for/else statement:
for d in dic.values():
if d['text'] == 'a':
print("Yes")
break
else:
print("No")
To go further, you could also:
use comprehension to perform the for loop and the == operation in a smarter Python way,
and use any to replace the if and break statements by a smarter Python built-in function.
This shorter code will in fact do exactly the same things as previous one, in a really pythonic way:
if any(d['text'] == 'a' for d in dic.values()):
print("Yes")
else:
print("No")
Enjoy !

if you are sure that the string you are looking for will no be in key names, then another simple solution is converting whole dict to str and searching for string
if 'a' in str(dic):
print("a is there")

You are treating the dictionary as a list. You cannot index into a dictionary, like this:
dic[0]
But you can get the value from a given key, like so:
dic["0"] # yields {'text': 'a', 'lang': 'es', 'rating': '4'}
As such, modify your for loop as follows:
for i in dic:
...

Related

Make a list consistent

I have 2 list ..
a = [{'Work': 'R'}, {'Area': 'S0'}, {'Type': 'PIV'}, {'Disc': 'LA'}, {'L': 'A'}]
b = [{'Key': '9', 'FileName': '123A.pdf', 'Code': '1', 'Type': 'PNHRG'}]
output -- [{'Key': '9', 'FileName': '123A.pdf', 'Code': '1', 'Type': 'PNHRG','Work': 'R','Area': 'S0','Type': 'PIV','Disc': 'LA','L': 'A'}]
I tried 'extend','append','insert' but did not get desired
output. tried all most all list operations with loops too. I am
not in position to change any kind of types for this.
Tried this solution too without luck. not sure where I am missing.
How do I make a flat list out of a list of lists?
You can use a doubly-nested dictionary comprehension, iterating over all the items in all the dicts in all the lists. Assuming multiple dicts in b might not be necessary, but makes it easier.
>>> a = [{'Work': 'R'}, {'Area': 'S0'}, {'Type': 'PIV'}, {'Disc': 'LA'}, {'L': 'A'}]
>>> b = [{'Key': '9', 'FileName': '123A.pdf', 'Code': '1', 'Type': 'PNHRG'}]
>>> [{k: v for lst in (a, b) for d in lst for k, v in d.items()}]
[{'Work': 'R', 'Area': 'S0', 'Type': 'PNHRG', 'Disc': 'LA', 'L': 'A', 'Key': '9', 'FileName': '123A.pdf', 'Code': '1'}]
Addendum: If there are any keys that appear in more than one dictionary, as is the case with "type" here, the value from the last dictionary that was iterated in the comprehension will be used, i.e. b here. If you need the value from a, do ... for lst in (b, a).... In your expected output, that key appears twice, but that is not possible unless you change the format, as in the other answer.
Extra from the answer provided by tobias_k:
If you want to combine both lists of dictionaries (and more when you have it) as a dictionary with a list of values in the same key. You can do this.
from collections import defaultdict
mydict = defaultdict(list)
for i in (a+b):
for key, value in i.items():
mydict[key].append(value)
mydict
Out[32]:
defaultdict(list,
{'Work': ['R'],
'Area': ['S0'],
'Type': ['PIV', 'PNHRG'],
'Disc': ['LA'],
'L': ['A'],
'Key': ['9'],
'FileName': ['123A.pdf'],
'Code': ['1']})

Is it possible to write a function that returns multiple values in a specific format?

I'm writing a function that returns two values which will form the key-value pair of a dictionary. This function will be used to create a dictionary with dictionary comprehension. However, using dictionary comprehension the pair of values need to be provided in the format 'key: value'. To accomplish this, I have to call the function twice. Once for the key, and once for the value. For example,
sample_list = [['John', '24', 'M', 'English'],
['Jeanne', '21', 'F', 'French'],
['Yuhanna', '22', 'M', 'Arabic']]
def key_value_creator(sample_list):
key = sample_list[0]
value = {'age': sample_list[1],
'gender': sample_list[2],
'lang': sample_list[3]}
return key, value
dictionary = {key_value_creator(item)[0]: \
key_value_creator(item)[1] for item in sample_list}
As you can see, the function is called twice to generate values that can be generated in one run. Is there a way to return the values in a format that can be usable by the comprehension? If that is possible, the function need only be called once, as such:
dictionary = {key_value_creator(item) for item in sample_list}
As far as I have seen, other ways of returning multiple values is to return them in the form of a dictionary or list,
return {'key': key, 'value': value}
return [key, value]
but either way, to access them we will have to call the function twice.
dictionary = {key_value_creator(item)['key']: \
key_value_creator(item)['value'] for item in sample_list}
dictionary = {key_value_creator(item)[0]: \
key_value_creator(item)[1] for item in sample_list}
Is there a way to format these values so that we can send them to the dictionary comprehension statement in the format that it requires?
EDIT:
Expected Output:
{ 'John': {'age': '24', 'gender': 'M', 'lang': 'English'},
'Jeanne': {'age': '21', 'gender': 'F', 'lang': 'French'},
'Yuhanna': {'age': '22', 'gender': 'M', 'lang': 'Arabic'}}
Just use the dict builtin function, expecting a sequence of (key, value) pairs as returned by your key_value_creator function and making a dict from those:
>>> dict(map(key_value_creator, sample_list))
{'Jeanne': {'age': '21', 'gender': 'F', 'lang': 'French'},
'John': {'age': '24', 'gender': 'M', 'lang': 'English'},
'Yuhanna': {'age': '22', 'gender': 'M', 'lang': 'Arabic'}}
Also works with a generator expression instead of map:
>>> dict(key_value_creator(item) for item in sample_list)
Or use a dictionary comprehension with a nested generator expression and tuple-unpacking:
>>> {k: v for k, v in (key_value_creator(item) for item in sample_list)}
Or without your key_value_creator function, just using a nested dictionary comprehension:
>>> {n: {"age": a, "gender": g, "lang": l} for n, a, g, l in sample_list}

How to access all dictionaries within a dictionary where a specific key has a particular value

I have a dictionary of dictionaries, with each nested dictionary having the exact same keys, like this:
all_dicts = {'a':{'name': 'A', 'city': 'foo'},
'b':{'name': 'B', 'city': 'bar'},
'c':{'name': 'C', 'city': 'bar'},
'd':{'name': 'B', 'city': 'foo'},
'e':{'name': 'D', 'city': 'bar'},
}
How to I get a list (or dictionary) of all the dictionaries where 'city' has value 'bar'?
The following code works, but isn't scalable:
req_key = 'bar'
selected = []
for one in all_dicts.keys():
if req_key in all_dicts[one]:
selected.append(all_dicts[one])
Say 'city' can have 50,000 unique values and the dictionary all_dicts contains 600,000 values, iterating over the dictionary for every single value of 'city' is not very efficient.
Is there a scalable and efficient way of doing this?
What you could do is create an index on that dictionary, like this:
cityIndex={}
for item in all_dicts.values():
if item['city'] in cityIndex:
cityIndex[item['city']].append(item)
else:
cityIndex[item['city']]=[item]
This will require some initial processing time as well as some additional memory, but afterwards it will be very fast. If you want all items with some cityName, you'll get them by doing:
mylist=cityIndex[cityName] if cityName in cityIndex else []
This gives you many benefits if all_dicts is built once and queried afterwards many times.
If all_dicts is being modified during the execution of your program, you will need some more code to maintain the cityIndex. If an item is added to all_dicts, just do:
if item['city'] in cityIndex:
cityIndex[item['city']].append(item)
else:
cityIndex[item['city']]=[item]
while if an item is removed, this is a straightforward way to remove it from the index as well (assuming the combination of 'name' and 'city' is unique among your items):
for i, val in enumerate(cityIndex[item['city']]):
if val['name']==item['name']:
break
del cityIndex[item['city']][i]
If there are many more queries than updates, you will still get a huge performance improvement.
You have to check all the values; there isn't an alternative to that. You could however use a vectorised approach - list comprehension - which is going to be much faster than a for loop:
selected = [d for d in all_dicts.values() if d['city']=='bar']
print(selected)
# [{'name': 'B', 'city': 'bar'}, {'name': 'C', 'city': 'bar'}, {'name': 'D', 'city': 'bar'}]
Using dict.values instead of accessing the dictionary keys also improves performance and is also memory efficient in Python 3.
Or use filter, in python 3:
>>> list(filter(lambda x: x['city']=='bar', all_dicts.values()))
# [{'name': 'D', 'city': 'bar'}, {'name': 'B', 'city': 'bar'}, {'name': 'C', 'city': 'bar'}]
Or with pandas:
import pandas as pd
df = pd.DataFrame(all_dicts).T
df[df.city=='bar'].T.to_dict()
# {'e': {'city': 'bar', 'name': 'D'}, 'c': {'city': 'bar', 'name': 'C'}, 'b': {'city': 'bar', 'name': 'B'}}
all_dicts = {'a':{'name': 'A', 'city': 'foo'},
'b':{'name': 'B', 'city': 'bar'},
'c':{'name': 'C', 'city': 'bar'},
'd':{'name': 'B', 'city': 'foo'},
'e':{'name': 'D', 'city': 'bar'},
}
citys = {}
for key, value in all_dicts.items():
citys[key] = value['city']
#{'a': 'foo', 'b': 'bar', 'e': 'bar', 'd': 'foo', 'c': 'bar'}
for key, value in citys.items():
if value == 'bar':
print(all_dicts[key])
out:
{'name': 'B', 'city': 'bar'}
{'name': 'D', 'city': 'bar'}
{'name': 'C', 'city': 'bar'}
Build an auxiliary dict to store city as index, and you can reference it very quickly.

How should I remove all dicts from a list that have None as one of their values?

Suppose I have a list like so:
[{'name': 'Blah1', 'age': x}, {'name': 'Blah2', 'age': y}, {'name': None, 'age': None}]
It is guaranteed that both 'name' and 'age' values will either be filled or empty.
I tried this:
for person_dict in list:
if person_dict['name'] == None:
list.remove(person_dict)
But obviously that does not work because the for loop skips over an index sometimes and ignores some blank people.
I am relatively new to Python, and I am wondering if there is a list method that can target dicts with a certain value associated with a key.
EDIT: Fixed tuple notation to list as comments pointed out
Just test for the presence of None in the dict's values to test ALL dict keys for the None value:
>>> ToD=({'name': 'Blah1', 'age': 'x'}, {'name': 'Blah2', 'age': 'y'}, {'name': None, 'age': None})
>>> [e for e in ToD if None not in e.values()]
[{'age': 'x', 'name': 'Blah1'}, {'age': 'y', 'name': 'Blah2'}]
Or, use filter:
>>> filter(lambda d: None not in d.values(), ToD)
({'age': 'x', 'name': 'Blah1'}, {'age': 'y', 'name': 'Blah2'})
Or, if it is a limited test to 'name':
>>> filter(lambda d: d['name'], ToD)
({'age': 'x', 'name': 'Blah1'}, {'age': 'y', 'name': 'Blah2'})
You can use list comprehension as a filter like this
[c_dict for c_dict in dict_lst if all(c_dict[key] is not None for key in c_dict)]
This will make sure that you get only the dictionaries where all the values are not None.
for index,person_dict in enumerate(lis):
if person_dict['name'] == None:
del lis[index]
you can also try
lis=[person_dict for person_dict in lis if person_dict['name'] != None]
never use List as variable
You can create new list with accepted data. If you have tuple then you have to create new list.
List comprehension could be faster but this version is more readable for beginners.
data = ({'name': 'Blah1', 'age': 'x'}, {'name': 'Blah2', 'age': 'y'}, {'name': None, 'age': None})
new_data = []
for x in data:
if x['name']: # if x['name'] is not None and x['name'] != ''
new_data.append(x)
print new_data

Extract all data with same key from the dictionary into another array

I just want to know a easy way to extract all data with same key "Cat" from the dictionary into another array.
From the below dictionary..
All data with key "Cat: Book" should be in a separate array and key "Cat: pen" in another.
This dictionary is dynamic and more different "Cat" values can come in.
category= [{'Cat':'Book','Item':'LongBook','Qty':'25'},
{'Cat':'Book','Item':'Diary','Qty':'20'},
{'Cat':'Pen','Item':'BallPoint','Qty':'30'}]
It is quite difficult to understand what do you really need. Anyway try to take a look at the following code that uses defaultdict:
category= [{'Cat':'Book','Item':'LongBook','Qty':'25'},
{'Cat':'Book','Item':'Diary','Qty':'20'},
{'Cat':'Pen','Item':'BallPoint','Qty':'30'}]
from collections import defaultdict
output = defaultdict(list)
for row in category:
output['Cat' + ':' + row['Cat']].append(row)
print output.values()
>>>[[{'Item': 'BallPoint', 'Qty': '30', 'Cat': 'Pen'}], [{'Item': 'LongBook', 'Qty': '25', 'Cat': 'Book'}, {'Item': 'Diary', 'Qty': '20', 'Cat': 'Book'}]]
Or better use groupby:
category= [{'Cat':'Book','Item':'LongBook','Qty':'25'},
{'Cat':'Book','Item':'Diary','Qty':'20'},
{'Cat':'Pen','Item':'BallPoint','Qty':'30'}]
from itertools import groupby
for key, vals in groupby(category, lambda x: x['Cat']):
print key, list(vals)
>>>Book [{'Item': 'LongBook', 'Qty': '25', 'Cat': 'Book'}, {'Item': 'Diary', 'Qty': '20', 'Cat': 'Book'}]
>>>Pen [{'Item': 'BallPoint', 'Qty': '30', 'Cat': 'Pen'}]

Categories

Resources