How to search if dictionary value contains certain string with Python - python

I have a dictionary with key-value pair. My value contains strings. How can I search if a specific string exists in the dictionary and return the key that correspond to the key that contains the value.
Let's say I want to search if the string 'Mary' exists in the dictionary value and get the key that contains it. This is what I tried but obviously it doesn't work that way.
#Just an example how the dictionary may look like
myDict = {'age': ['12'], 'address': ['34 Main Street, 212 First Avenue'],
'firstName': ['Alan', 'Mary-Ann'], 'lastName': ['Stone', 'Lee']}
#Checking if string 'Mary' exists in dictionary value
print 'Mary' in myDict.values()
Is there a better way to do this since I may want to look for a substring of the value stored ('Mary' is a substring of the value 'Mary-Ann').

You can do it like this:
#Just an example how the dictionary may look like
myDict = {'age': ['12'], 'address': ['34 Main Street, 212 First Avenue'],
'firstName': ['Alan', 'Mary-Ann'], 'lastName': ['Stone', 'Lee']}
def search(values, searchFor):
for k in values:
for v in values[k]:
if searchFor in v:
return k
return None
#Checking if string 'Mary' exists in dictionary value
print search(myDict, 'Mary') #prints firstName

I am a bit late, but another way is to use list comprehension and the any function, that takes an iterable and returns True whenever one element is True :
# Checking if string 'Mary' exists in the lists of the dictionary values
print any(any('Mary' in s for s in subList) for subList in myDict.values())
If you wanna count the number of element that have "Mary" in them, you can use sum():
# Number of sublists containing 'Mary'
print sum(any('Mary' in s for s in subList) for subList in myDict.values())
# Number of strings containing 'Mary'
print sum(sum('Mary' in s for s in subList) for subList in myDict.values())
From these methods, we can easily make functions to check which are the keys or values matching.
To get the keys containing 'Mary':
def matchingKeys(dictionary, searchString):
return [key for key,val in dictionary.items() if any(searchString in s for s in val)]
To get the sublists:
def matchingValues(dictionary, searchString):
return [val for val in dictionary.values() if any(searchString in s for s in val)]
To get the strings:
def matchingValues(dictionary, searchString):
return [s for s i for val in dictionary.values() if any(searchString in s for s in val)]
To get both:
def matchingElements(dictionary, searchString):
return {key:val for key,val in dictionary.items() if any(searchString in s for s in val)}
And if you want to get only the strings containing "Mary", you can do a double list comprehension :
def matchingStrings(dictionary, searchString):
return [s for val in dictionary.values() for s in val if searchString in s]

Klaus solution has less overhead, on the other hand this one may be more readable
myDict = {'age': ['12'], 'address': ['34 Main Street, 212 First Avenue'],
'firstName': ['Alan', 'Mary-Ann'], 'lastName': ['Stone', 'Lee']}
def search(myDict, lookup):
for key, value in myDict.items():
for v in value:
if lookup in v:
return key
search(myDict, 'Mary')

import re
for i in range(len(myDict.values())):
for j in range(len(myDict.values()[i])):
match=re.search(r'Mary', myDict.values()[i][j])
if match:
print match.group() #Mary
print myDict.keys()[i] #firstName
print myDict.values()[i][j] #Mary-Ann

>>> myDict
{'lastName': ['Stone', 'Lee'], 'age': ['12'], 'firstName': ['Alan', 'Mary-Ann'],
'address': ['34 Main Street, 212 First Avenue']}
>>> Set = set()
>>> not ['' for Key, Values in myDict.items() for Value in Values if 'Mary' in Value and Set.add(Key)] and list(Set)
['firstName']

For me, this also worked:
def search(myDict, search1):
search.a=[]
for key, value in myDict.items():
if search1 in value:
search.a.append(key)
search(myDict, 'anyName')
print(search.a)
search.a makes the list a globally available
if a match of the substring is found in any value, the key of that
value will be appended to a

Following is one liner for accepted answer ... (for one line lovers ..)
def search_dict(my_dict,searchFor):
s_val = [[ k if searchFor in v else None for v in my_dict[k]] for k in my_dict]
return s_val

To provide a more general solution for others using this post to do similar or more complex python dictionary searches: you can use dictpy
import dictpy
myDict = {'age': ['12'], 'address': ['34 Main Street, 212 First Avenue'],
'firstName': ['Alan', 'Mary-Ann'], 'lastName': ['Stone', 'Lee']}
search = dictpy.DictSearch(data=myDict, target='Mary-Ann')
print(search.result) # prints -> [firstName.1, 'Mary-Ann']
The first entry in the list is the target location: dictionary key "firstName" and position 1 in the list. The second entry is the search return object.
The benefit of dictpy is it can find multiple 'Mary-Ann' and not just the first one. It tells you the location in which it found it, and you can search more complex dictionaries (more levels of nesting) and change what the return object is.

import re
for i in range(len(myDict.values())):
for j in range(len(myDict.values()[i])):
match=re.search(r'Mary', myDict.values()[i][j])
if match:
print match.group() #Mary
print myDict.keys()[i] #firstName
print myDict.values()[i][j] #Mary-Ann

def search(myDict, lookup):
a=[]
for key, value in myDict.items():
for v in value:
if lookup in v:
a.append(key)
a=list(set(a))
return a
if the research involves more keys maybe you should create a list with all the keys

import json
'mtach' in json.dumps(myDict)
is true if found

Related

Case Insensitive Dictionary Iteration

I have a dictionary which has peoples' first names as keys. Each name has a capitalised first letter (James, Ben, John, etc).
I use list comprehension to check if any keys are in a string:
[val for key, val in name_dict.items() if key in new_message]
The issue is that sometimes the names appear in new_message without capitalised first letters (james, ben, john, etc). I could add these variations to the dictionary but that sould invovle a lot of work.
Is there a simple way to iterate over the dictionary keys in a case insensitive way?
Use title() on new_message, it will capitalize all the words in the string
new_message = 'James, ben, JOHN'
print(new_message.title()) # James, Ben, John
You can just lower the text while iterating,
new_message = [x.lower() for x in new_message]
[val for key, val in name_dict.items() if key.lower() in new_message]
( Assuming if the new_message is list, for string it will be just new_message.lower() )
This will make the comparison case-insensitive.
Full example
>>> new_message = ['John', 'JameS', 'BEN']
>>> name_dict = {'john': 1, 'Ben': 2, 'JaMES': 3}
>>>
>>> [x.lower() for x in new_message]
['john', 'james', 'ben']
>>> new_message = [x.lower() for x in new_message]
>>> [val for key, val in name_dict.items() if key.lower() in new_message]
[1, 2, 3]
>>>

Define a function to generate a dictionary

def create_dictionary(params)
How do I generate a dictionary from a string value which comes
from its parameter named params. The dictionary is created with the use of zip function to handle the keys and values which are generated from params
How do I make the output of this:
print(create_dictionary('name:= Jack ; grade:=3'))
To be like this:
{'name': 'Jack', 'grade': '3'}
def create_dictionary(string1):
s = string1.split(';')
dic = {}
for i in s:
key, value = i.split(':=')
dic[key] = value
return dic
If the string's syntax is same all the time, you can split the string accordingly. In this case:
First split them with ; to separate dictionary values.
Then split rest with := to get the key and the value. You may want to trim the resulting key and value to remove the white spaces around.
Note: If data itself contains the ; or :=, this solution will fail.
Edit
In addition to the Daniel Paul's answer for removing the white spaces:
def create_dictionary(string1):
s = string1.split(';')
dic = {}
for i in s:
key, value = i.split(':=')
dic[key.strip()] = value.strip()
return dic
Before: {'name': ' Jack ', ' grade': '3'} after {'name': 'Jack', 'grade': '3'}
One-liner using python's dict comprehension:
def create_dictionary(string):
{a.strip():b.strip() for a,b in [i.split(':=') for i in string.split(';')]}
You can use the split method and split := or you can follow up my code
def dict_fun(*args):
dict = {}
cnt = 0
count = 0
while cnt < len(args):
for i in args[cnt]:
if i == ':':
print(count)
dict[args[cnt][0:count]] = args[cnt].strip().replace(' ','')[count+2:]
break
else:
count += 1
cnt += 1
count = 0
return(dict)
print(dict_fun('name:=Jack', 'grade:=3'))
OUTPUT
{'name': 'Jack', 'grade': '3'}
Now you can give as many argument as many you want inside dict_fun('name:=jack','grade:=3',etc..) so as of taking single string now you can give mutiple string argument to the function and it would return you the dict of that string..

assign values based on a dict

I have a list1 with names:
["SAM","TOM","LOUIS"]
And I have a dict1 like this (where in the list of values there are no repeated names:
{"NICE": ["SAM", "MAIK", "CARL", "LAURA", "MARTH"],
"BAD": ["LOUIS", "TOM", "KEVIN"],
"GOOD": ["BILL", "JEN", "ALEX"]}
How could I iterate throught the list1 so that if any of the names appear in any of the lists of the dict1 it assigns the corresponding key of the dict?
I am looking forward to generate the following output:
["NICE","BAD","BAD"]
which would correspond to the keys of the values that appear in the list : SAM, TOM , LOUIS .
This is what I thought about:
lista=[]
for k,v in dict1:
for values in arr1:
if values in v:
lista.append(v)
lista
However not sure how to iterate over the different v, how can I get the desired output in an efficient manner?
You can create an intermediate dict that maps names to their keys in dict1:
categories = {name: category for category, names in dict1.items() for name in names}
so that you can map the names in list1 to their respective keys efficiently with:
[categories[name] for name in list1]
which returns:
['NICE', 'BAD', 'BAD']
I think you need the items() function for dictionaries here. For each name, iterate over all of the dictionary pairs and stop when a match is found, adding the corresponding adjective to your list.
lista = []
for name in list1:
for adjective, names in dict1.items():
if name in names:
lista.append(adjective)
break
return lista
There are 2 ways to achieve your result.
The way you intended was to use dict1.items(). Unfortunately, that way is computationally slow, so for the sake of completeness I'll add the the more efficient way:
# First, convert the dict to a different representation:
from itertools import chain, repeat
# Change {k: [v1,v2], k2: [v3,v4]} to {v1: k, v2: k, v3: k2, v4: k2}
name_to_adjective = dict(chain.from_iterable(zip(v, repeat(k)) for k, v in a.items()))
Name_to_adjective is now equal to this:
{'ALEX': 'GOOD',
'BILL': 'GOOD',
'CARL': 'NICE',
'JEN': 'GOOD',
'KEVIN': 'BAD',
'LAURA': 'NICE',
'LOUIS': 'BAD',
'MAIK': 'NICE',
'MARTH': 'NICE',
'SAM': 'NICE',
'TOM': 'BAD'}
Then, you can get your result in one run:
result = [name_to_adjective[name] for name in list1]
I believe the following will give you your desired output:
L = ["SAM","TOM","LOUIS"]
D = {"NICE": ["SAM", "MAIK", "CARL", "LAURA", "MARTH"]
, "BAD": ["LOUIS", "TOM", "KEVIN"]
, "GOOD": ["BILL", "JEN", "ALEX"]}
lista = []
for key in D.keys():
for name in L:
if name in D[key]:
lista.append(key)
print(lista)
The D.keys() part gives you a list of the keys in a friendly manner (e.g, ["NICE", "BAD", "GOOD"]).
You iterate over this and then look for each name from L (in order) in the dictionary.
This is not the most efficient way to do this, however, it's a more straightforward approach.

How to remove short strings from a list which is a value of a dict

I want to make a function called remove_short_synonyms() which is passed a dict
as a parameter. The keys of the parameter dict are words and the
corresponding values are lists of synonyms. The function removes all the
synonyms which have less than 7 characters from each corresponding list
of synonyms.
If this is the dict:
synonyms_dict = {'beautiful': ['pretty', 'lovely', 'handsome', 'dazzling', 'splendid', 'magnificent']}
How can I get this as the output?
{'beautiful': ['dazzling', 'handsome', 'magnificent', 'splendid']}
I think your question is more proper to be titled as Remove values from a list instead of dict.
You can use remove, del or pop to remove element in a python list.
Difference between del, remove and pop on lists
Or in a more pythonic way, i think, is
dict['beautiful'] = [item for item in dict['beautiful'] if len(item)>=7]
Make use of dict comprehension and list comprehension.
synonyms_dict = {'beautiful' : ['pretty', 'lovely', 'handsome', 'dazzling', 'splendid', 'magnificent']}
synonyms_dict = {k:[v1 for v1 in v if len(v1) >= 7] for k, v in synonyms_dict.items()}
print(synonyms_dict)
# {'beautiful': ['handsome', 'dazzling', 'splendid', 'magnificent']}
​
Assuming you have python>=3.x, a more readable solution for a beginner would be:
synonyms_dict = {'beautiful' : ['pretty', 'lovely', 'handsome', 'dazzling', 'splendid', 'magnificent']}
new_list = []
for key,value in synonyms_dict.items():
for i in range(len(value)):
if len(value[i]) >= 7:
new_list.append(value[i])
synonyms_dict['beautiful'] = new_list
print(synonyms_dict)
Here's a function that modifies the existing dictionary rather than replacing it. This can be useful if you have multiple references to the same dictionary.
synonyms_dict = {
'beautiful' : ['pretty', 'lovely', 'handsome', 'dazzling', 'splendid', 'magnificent']
}
def remove_short_synonyms(d, minlen=7):
for k, v in d.items():
d[k] = [word for word in v if len(word) >= minlen]
remove_short_synonyms(synonyms_dict)
print(synonyms_dict)
output
{'beautiful': ['handsome', 'dazzling', 'splendid', 'magnificent']}
Note that this code does replace the existing lists in the dictionary with new lists. You could keep the old list objects, if you really need to do that, by changing the assignment line to
d[k][:] = [word for word in v if len(word) >= minlen]
although that will be slightly slower, and there's probably no reason to do this.
def remove_short_synonyms(self, **kwargs):
dict = {}
word_list = []
for key, value in synonyms_dict.items():
for v in value:
if len(v) > 7:
word_list.append(v)
dict[key] = word_list
print dict
remove_short_synonyms(synonyms_dict)

Python - Return list of dictionaries with unique Key:Value pair

I have a long list of dictionaries that for the most part do not overlap. However, some of the dictionaries have the same 'Name' field and I'd only like unique names in the list of dictionaries. I'd like the first occurrence of the name to be the one that stays and any thereafter be deleted from the list.
I've put a short list below to illustrate the scenario:
myList = [
{'Name':'John', 'Age':'50', 'Height':'70'},
{'Name':'Kathy', 'Age':'43', 'Height':'65'},
{'Name':'John','Age':'46','Height':'68'},
{'Name':'John','Age':'50','Height':'72'}
]
I'd like this list to return the first 'John' and Kathy, but not the second or third Johns and their related information.
An acceptable, but not optimal solution would also be not having dictionaries with the same name next to each other.
You could run over the list and keep a set of unique names. Every time you encounter a new name (i.e., a name that isn't in the set), you add it to the set and the respective dict to the result:
def uniqueNames(dicts):
names = set()
result = []
for d in dicts:
if not d['Name'] in names:
names.add(d['Name'])
result.append(d)
return result
You can easily write a for-loop for this.
def getName(name):
'''Gets first occurence of name in list of dicts.'''
for i in myList:
if i['Name'] == name:
return i
Initial list:
my_list = [
{'Name':'John', 'Age':'50', 'Height':'70'},
{'Name':'Kathy', 'Age':'43', 'Height':'65'},
{'Name':'John','Age':'46','Height':'68'},
{'Name':'John','Age':'50','Height':'72'}
]
The logical (potentially newbie-friendlier) way:
names = set()
new_list = []
for d in my_list:
name = d['Name']
if name not in names:
new_list.append(d)
names.add(d['Name'])
print new_list # [{'Age': '50', 'Name': 'John', 'Height': '70'}, {'Age': '43', 'Name': 'Kathy', 'Height': '65'}]
A one-liner way:
new_list = {d['Name']: d for d in reversed(my_list)}.values()
print new_list # [{'Age': '43', 'Name': 'Kathy', 'Height': '65'}, {'Age': '50', 'Name': 'John', 'Height': '70'}]
Note: The one-liner will contain the first occurrence of each name, but it will return an arbitrarily ordered list.

Categories