Can someone assist me on the proper placement of an if statement on my dict. I am try to flag any user that is NY and have them come first in the dictionary. I am able to get sort by name which I require as well but not clear how to flag any NY users
names_states = {
'user1': 'CA',
'user2': 'NY',
'user7': 'CA',
'guest': 'MN',
}
for key in sorted(names_states.iterkeys()):
2 ... print "%s : %s" %(key, names_states[key])
3 ...
4 user1 : CA
5 user2 : NY
6 guest : MN
7 user7 : CA
sorted(names_states.iteritems(), key=lambda x: (x[1] != 'NY', x[0]))
Here is an approach that first pushes the NY values to the top, while still sorting by user name as the first key, and the state as the secondary key:
{'bill': 'NY',
'frank': 'NY',
'guest': 'MN',
'user1': 'CA',
'user2': 'NY',
'user7': 'CA'}
def keyFunc(x):
if names_states[x] == 'NY':
return (False, x)
return (x, names_states[x])
sorted(names_states.iterkeys(), key=keyFunc)
# ['bill', 'frank', 'user2', 'guest', 'user1', 'user7']
Note: Sticking with the key approach here is faster than defining a custom cmp function. The key function will only be run once for each item, whereas a cmp function will be run every single combination.
This does what you asked for-- All of newyorkers first, sorted by name, and then everyone else sorted by name too.
names_states = {
'user1': 'CA',
'user2': 'NY',
'user7': 'CA',
'guest': 'MN',
}
def ny_compare(x, y):
if names_states[x] == 'NY':
if names_states[y] == 'NY':
return cmp(x,y)
else:
return -1
elif names_states[y] == 'NY':
return 1
return cmp(x,y)
for key in sorted(names_states.iterkeys(), cmp=ny_compare):
print "%s : %s" %(key, names_states[key])
Using tuple unpacking may make it clearer
sorted(names_states.iteritems(), key=lambda (name, state): (state != 'NY', name))
for key, value in sorted(names_states.iteritems(),
key=lambda (key, value): ((0 if value == 'NY' else 1), key)):
print((key,value))
Here iteritems() gets us an iterator over the key/value pairs in the dictionary. We can then sort those key/value pairs. The lambda expression creates a set of keys that will sort the list as you say. It does this by returning tuples, whose first element is a number based on whether the value is 'NY' or not. Basically, I'm using the fact that tuples are sorted by their first element and then their second and so on.
That said, this is beginning to look quite messy, so you may want to find a more verbose, but clearer way to do this.
You may wish to subclass dict and add an additional method that exposes the keys, values or key/value pairs in the order you desire.
That way you can have something as simple as:
names_states = MyDict({
'user1': 'CA',
'user2': 'NY',
'user7': 'CA',
'guest': 'MN',
})
for key, value in names_states.iteritems_with_given_values_first('NY'):
...
class MyDict(dict):
def __init__(self, *args, **kwargs):
super(MyDict, self).__init__(*args, **kwargs)
def iteritems_with_given_values_first(self, preferred_value):
for key, value in sorted(names_states.iteritems(),
key=lambda (key, value): ((0 if value == preferred_value else 1), key)):
yield key, value
Related
I have been trying to figure out how to return the key which has the nth biggest value in a nested dictionary. What causes problems for me is if there's some missing values like
my_dict = {'0': {'name': 'A', 'price': 50}, '1': {'name': 'B', 'price': 20}, '2': {'name': 'C'}, '3': {'name': 'D', 'price': 10}}
If every price existed, I could use code such as this to get the correct key:
sorted_list = sorted(my_dict.items(), key=lambda item: item[1]['price'])
print(sorted_list[-number_im_using_to_find][1]['name'])
How to account for missing values in an efficient way?
you can use dict.get to achieve this:
get(key[, default])
Return the value for key if key is in the dictionary, else default. If default is not given, it defaults to None, so that this method never raises a KeyError.
Here is a code implementation using dict.get:
default_value = -1
key = lambda item: item[1].get('price', default_value)
sorted_list = sorted(my_dict.items(), key=key)
If you want just sorted values you could remove entirely the index [1]:
default_value = -1
key = lambda value: value.get('price', default_value)
sorted_list = sorted(my_dict.values(), key=key)
EDIT: if you want to be sortable with NoneType in the list, you could use something like:
>>> mylist = [3, 1, None, None, 2, 0]
>>> mylist.sort(key=lambda x: x or -1)
>>> mylist
[None, None, 0, 1, 2, 3]
Use get with a default value, in this case 0 or even a negative value to put the elements with missing prices all the way down the sorted list.
lambda item: item[1].get('price', 0)
I have such a json file that combines all previous data storing versions. An example would be like this:
myList = {1: {'name': 'John', 'age': '27', 'class'= '2', 'drop' = True},
2: {'name': 'Marie', 'other_info': {'age': '22', 'class'= '3', 'dropped'= True }},
3: {'name': 'James', 'other_info': {'age': '23', 'class'= '1', 'is_dropped'= False}},
4: {'name': 'Lucy', 'some_info': {'age': '20', 'class'= '4', 'other_branch': {'is_dropped' = True, 'how_drop'= 'Foo'}}}}
I want to reach the information that contains drop in the key or subkey . I don't know all the dictionary structures, there can be 20 or more. All I know is that they all contain the phrase 'drop'. There might be other phrases that might contain the phrase 'drop', as well but they are not too much. If there are multiple drops, I can manually adjust which one to take.
I tried to flatten, but after flattening every dictionary item had a different key name.
There are other information I'd like to reach but most of these attributes have the similar problem, too.
I want to get the True, True, False, True values in the drop, dropped, and is_dropped keys.
How can I reach this node?
You can use recursion to solve this:
def get_drop(dct):
for key, val in dct.items():
if isinstance(key, str) and 'drop' in key and isinstance(val, bool):
yield val
elif isinstance(val, dict):
yield from get_drop(val)
print(list(get_drop(myList)))
[True, True, False, True]
Create a recursive function to search and adds to the incrementally keys. Without putting in details in safety checks, you can do something like this:
def find(input_dict, base='', search_key='drop'):
found_paths = []
if search_key in input_dict.keys():
found_paths.append(base)
for each_key in input_dict.keys():
if isinstance(input_dict[each_key], dict):
new_base = base + '.' + each_key
found_paths += find(input_dict[each_key], base=new_base, search_key=search_key)
return found_paths
This question already has answers here:
Nested dictionary value from key path
(9 answers)
get dictionary key by path (string)
(7 answers)
Closed 2 years ago.
Pretty new to python, but pretty familiar with basic syntax, so I apologize if the description isn't completely using the correct terms.
If I have a nested dictionary called standings,
which contains a value that is a dictionary called Team,
which contains a value called name that can be accessed with the following:
for dicts in standings:
print(dicts["team"]["name"])
How can I represent it like the following:
teamName = ["team"]["name"]
for dicts in standings:
print(dicts+teamName)
so basically want to change From:
dicts["team"]["name"]
to
dicts+variableName
to give the same result.
Dictionary snippet:
{'rank': 1, 'team': {'id': 50, 'name': 'Manchester City'}}
I've tried a few different ways, but can't see to figure it out without getting an error, wondering if maybe it's just not possible? (although I'm sure its just a user error on my part)
Thanks!
Your description of the data structure does not make much sense, but assuming dicts is a dictionary that contains a key team with a value that contains the key name. You can perform dynamic nested lookup using a list of keys and recursion-
def nested_lookup(d, keys):
if len(keys) == 0:
return d
item = d[keys[0]]
rest_keys = keys[1:]
return nested_lookup(item, rest_keys)
Or, if you prefer succinct-ness, a lambda function-
nested_lookup = lambda d, ks: nested_lookup(d[ks[0]], ks[1:]) if len(ks) > 0 else d
You can use it like-
>>> nested_lookup({ 'team': { 'name': 5 } }, ['team', 'name'])
5
>>> nested_lookup({ 'team': { 'name': 5 } }, ['team'])
{'name': 5}
>>> nested_lookup({ 'team': { 'name': { 'more': [42, 47] } } }, ['team', 'name', 'more'])
[42, 47]
Similar to the previous answer, but without use of a recursion and with error handling. In case of a missing key None value will be returned.
d = {'rank': 1, 'team': {'id': 50, 'name': 'Manchester City'}}
def get_nested(d, *keys):
result = d.copy()
for key in keys:
try:
result = result[key]
except KeyError:
return None
return result
print(get_nested(d, 'rank'))
print(get_nested(d, 'team', 'id'))
print(get_nested(d, 'team', 'name'))
print(get_nested(d, 'team', 'league'))
print(get_nested(d, 'non_existent', 'name'))
keys = 'team', 'name'
print(get_nested(d, *keys))
Output
1
50
Manchester City
None
None
Manchester City
How would I insert a key-value pair at a specified location in a python dictionary that was loaded from a YAML document?
For example if a dictionary is:
dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'}
I wish to insert the element 'Phone':'1234'
before 'Age', and after 'Name' for example. The actual dictionary I shall be working on is quite large (parsed YAML file), so deleting and reinserting might be a bit cumbersome (I don't really know).
If I am given a way of inserting into a specified position in an OrderedDict, that would be okay, too.
On python < 3.7 (or cpython < 3.6), you cannot control the ordering of pairs in a standard dictionary.
If you plan on performing arbitrary insertions often, my suggestion would be to use a list to store keys, and a dict to store values.
mykeys = ['Name', 'Age', 'Class']
mydict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'} # order doesn't matter
k, v = 'Phone', '123-456-7890'
mykeys.insert(mykeys.index('Name')+1, k)
mydict[k] = v
for k in mykeys:
print(f'{k} => {mydict[k]}')
# Name => Zara
# Phone => 123-456-7890
# Age => 7
# Class => First
If you plan on initialising a dictionary with ordering whose contents are not likely to change, you can use the collections.OrderedDict structure which maintains insertion order.
from collections import OrderedDict
data = [('Name', 'Zara'), ('Phone', '1234'), ('Age', 7), ('Class', 'First')]
odict = OrderedDict(data)
odict
# OrderedDict([('Name', 'Zara'),
# ('Phone', '1234'),
# ('Age', 7),
# ('Class', 'First')])
Note that OrderedDict does not support insertion at arbitrary positions (it only remembers the order in which keys are inserted into the dictionary).
You will have to initialize your dict as OrderedDict. Create a new empty OrderedDict, go through all keys of the original dictionary and insert before/after when the key name matches.
from pprint import pprint
from collections import OrderedDict
def insert_key_value(a_dict, key, pos_key, value):
new_dict = OrderedDict()
for k, v in a_dict.items():
if k==pos_key:
new_dict[key] = value # insert new key
new_dict[k] = v
return new_dict
mydict = OrderedDict([('Name', 'Zara'), ('Age', 7), ('Class', 'First')])
my_new_dict = insert_key_value(mydict, "Phone", "Age", "1234")
pprint(my_new_dict)
Had the same issue and solved this as described below without any additional imports being required and only a few lines of code.
Tested with Python 3.6.9.
Get position of key 'Age' because the new key value pair should get inserted before
Get dictionary as list of key value pairs
Insert new key value pair at specific position
Create dictionary from list of key value pairs
mydict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'}
print(mydict)
# {'Name': 'Zara', 'Age': 7, 'Class': 'First'}
pos = list(mydict.keys()).index('Age')
items = list(mydict.items())
items.insert(pos, ('Phone', '123-456-7890'))
mydict = dict(items)
print(mydict)
# {'Name': 'Zara', 'Phone': '123-456-7890', 'Age': 7, 'Class': 'First'}
Edit 2021-12-20:
Just saw that there is an insert method available ruamel.yaml, see the example from the project page:
import sys
from ruamel.yaml import YAML
yaml_str = """\
first_name: Art
occupation: Architect # This is an occupation comment
about: Art Vandelay is a fictional character that George invents...
"""
yaml = YAML()
data = yaml.load(yaml_str)
data.insert(1, 'last name', 'Vandelay', comment="new key")
yaml.dump(data, sys.stdout)
This is a follow-up on nurp's answer. Has worked for me, but offered with no warranty.
# Insert dictionary item into a dictionary at specified position:
def insert_item(dic, item={}, pos=None):
"""
Insert a key, value pair into an ordered dictionary.
Insert before the specified position.
"""
from collections import OrderedDict
d = OrderedDict()
# abort early if not a dictionary:
if not item or not isinstance(item, dict):
print('Aborting. Argument item must be a dictionary.')
return dic
# insert anywhere if argument pos not given:
if not pos:
dic.update(item)
return dic
for item_k, item_v in item.items():
for k, v in dic.items():
# insert key at stated position:
if k == pos:
d[item_k] = item_v
d[k] = v
return d
d = {'A':'letter A', 'C': 'letter C'}
insert_item(['A', 'C'], item={'B'})
## Aborting. Argument item must be a dictionary.
insert_item(d, item={'B': 'letter B'})
## {'A': 'letter A', 'C': 'letter C', 'B': 'letter B'}
insert_item(d, pos='C', item={'B': 'letter B'})
# OrderedDict([('A', 'letter A'), ('B', 'letter B'), ('C', 'letter C')])
Would this be "pythonic"?
def add_item(d, new_pair, old_key): #insert a newPair (key, value) after old_key
n=list(d.keys()).index(old_key)
return {key:d.get(key,new_pair[1]) for key in list(d.keys())[:n+1] +[new_pair[0]] + list(d.keys())[n+1:] }
INPUT: new_pair=('Phone',1234) , old_key='Age'
OUTPUT: {'Name': 'Zara', 'Age': 7, 'Phone': 1234, 'Class': 'First'}
Simple reproducible example (using zip() for unpacking and packing)
### Task - Insert 'Bangladesh':'Dhaka' after 'India' in the capitals dictinary
## Given dictionary
capitals = {'France':'Paris', 'United Kingdom':'London', 'India':'New Delhi',
'United States':'Washington DC','Germany':'Berlin'}
## Step 1 - Separate into 2 lists containing : 1) keys, 2) values
country, cap = (list(tup) for tup in zip(*capitals.items()))
# or
country, cap = list(map(list, zip(*capitals.items())))
print(country)
#> ['France', 'United Kingdom', 'India', 'United States', 'Germany']
print(cap)
#> ['Paris', 'London', 'New Delhi', 'Washington DC', 'Berlin']
## Step 2 - Find index of item before the insertion point (from either of the 2 lists)
req_pos = country.index('India')
print(req_pos)
#> 2
## Step 3 - Insert new entry at specified position in both lists
country.insert(req_pos+1, 'Bangladesh')
cap.insert(req_pos+1, 'Dhaka')
print(country)
#> ['France', 'United Kingdom', 'India', 'Bangladesh', 'United States', 'Germany']
print(cap)
#> ['Paris', 'London', 'New Delhi', 'Dhaka', 'Washington DC', 'Berlin']
## Step 4 - Zip up the 2 lists into a dictionary
capitals = dict(zip(country, cap))
print(capitals)
#> {'France': 'Paris', 'United Kingdom': 'London', 'India': 'New Delhi', 'Bangladesh': 'Dhaka', 'United States': 'Washington DC', 'Germany': 'Berlin'}
Once your have used load() (without option Loader=RoundTripLoader) and your data is in a dict() it is to late, as the order that was available in the YAML file is normally gone (the order depending on the actual keys used, the python used (implementation, version and possible compile options).
What you need to do is use round_trip_load():
import sys
from ruamel import yaml
yaml_str = "{'Name': 'Zara', 'Age': 7, 'Class': 'First'}"
data = yaml.round_trip_load(yaml_str)
pos = list(data.keys()).index('Age') # determine position of 'Age'
# insert before position of 'Age'
data.insert(pos, 'Phone', '1234', comment='This is the phone number')
data.fa.set_block_style() # I like block style
yaml.round_trip_dump(data, sys.stdout)
this will invariable give:
Name: Zara
Phone: '1234' # This is the phone number
Age: 7
Class: First
Under the hood round_trip_dump() transparently gives you back a subclass of orderddict to make this possible (which actual implementation is dependent on your Python version).
Since your elements comes in pairs, I think this will could work.
dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'}
new_element = { 'Phone':'1234'}
dict = {**dict,**new_element}
print(dict)
This is the output I got:
{'Name': 'Zara', 'Age': 7, 'Class': 'First', 'Phone': '1234'}
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