Removing nested dict items with dict comprehension - python

I have two dicts:
blocked = {'-5.00': ['121', '381']}
all_odds = {'-5.00': '{"121":[1.85,1.85],"381":[2.18,1.73],"16":[2.18,1.61],"18":\
[2.12,1.79]}'}
I want to first check whether the .keys() comparision (==) returns True, here it does (both -5.00) then I want to remove all items from all_odds that has the key listed in blocked.values() .
For the above it should result in:
all_odds_final = {'-5.00': '{"16":[2.18,1.61],"18": [2.12,1.79]}'}
I tried for loop:
if blocked.keys() == all_odds.keys():
for value in blocked.values():
for v in value:
for val in all_odds.values():
val = eval(val)
if val.has_key(v):
del val[v]
which you know is very ugly plus it's not working properly yet.

First, make the string a dictionary with ast.literal_eval(). Don't use eval():
>>> import ast
>>> all_odds['-5.00'] = ast.literal_eval(all_odds['-5.00'])
Then you can use a dictionary comprehension:
>>> if blocked.keys() == all_odds.keys():
... print {blocked.keys()[0] : {k:v for k, v in all_odds.values()[0].iteritems() if k not in blocked.values()[0]}}
...
{'-5.00': {'18': [2.12, 1.79], '16': [2.18, 1.61]}}
But if you want the value of -5.00 as a string...
>>> {blocked.keys()[0]:str({k: v for k, v in all_odds.values()[0].iteritems() if k not in blocked.values()[0]})}
{'-5.00': "{'18': [2.12, 1.79], '16': [2.18, 1.61]}"}

Here's how you can do the same in about 2 lines. I'm not going to use ast, or eval here, but you can add that if you want to use that.
>>> blocked = {'-5.00': ['121', '381']}
>>> all_odds = {'-5.00': {'121':[1.85,1.85],'381':[2.18,1.73],'16':[2.18,1.61],'18':\
... [2.12,1.79]}}
>>> bkeys = [k for k in all_odds.keys() if k in blocked.keys()]
>>> all_odds_final = {pk: {k:v for k,v in all_odds.get(pk).items() if k not in blocked.get(pk)} for pk in bkeys}
>>> all_odds_final
{'-5.00': {'18': [2.12, 1.79], '16': [2.18, 1.61]}}

This seems to work:
blocked = {'-5.00': ['121', '381']}
all_odds = {'-5.00': {"121":[1.85,1.85],"381":[2.18,1.73],"16":[2.18,1.61],"18":\
[2.12,1.79]}}
all_odds_final = dict(all_odds)
for key, blocks in blocked.iteritems():
map(all_odds_final[key].pop,blocks,[])
If you do not want to copy the dictionary, you can just pop items out of the original all_odds dictionary:
for key, blocks in blocked.iteritems():
map(all_odds[key].pop,blocks,[])
The empty list in the map function is so pop gets called with None as it's second argument. Without it pop only gets one argument and will return an error if the key is not present.

Related

Get specific key of a nested iterable and check if its value exists in a list

I am trying to access a specific key in a nest dictionary, then match its value to a string in a list. If the string in the list contains the string in the dictionary value, I want to override the dictionary value with the list value. below is an example.
my_list = ['string1~', 'string2~', 'string3~', 'string4~', 'string5~', 'string6~']
my_iterable = {'A':'xyz',
'B':'string6',
'C':[{'B':'string4', 'D':'123'}],
'E':[{'F':'321', 'B':'string1'}],
'G':'jkl'
}
The key I'm looking for is B, the objective is to override string6 with string6~, string4 with string4~, and so on for all B keys found in the my_iterable.
I have written a function to compute the Levenshtein distance between two strings, but I am struggling to write an efficient ways to override the values of the keys.
def find_and_replace(key, dictionary, original_list):
for k, v in dictionary.items():
if k == key:
#function to check if original_list item contains v
yield v
elif isinstance(v, dict):
for result in find_and_replace(key, v, name_list):
yield result
elif isinstance(v, list):
for d in v:
if isinstance(d, dict):
for result in find_and_replace(key, d, name_list):
yield result
if I call
updated_dict = find_and_replace('B', my_iterable, my_list)
I want updated_dict to return the below:
{'A':'xyz',
'B':'string6~',
'C':[{'B':'string4~', 'D':'123'}],
'E':[{'F':'321', 'B':'string1~'}],
'G':'jkl'
}
Is this the right approach to the most efficient solution, and how can I modify it to return a dictionary with the updated values for B?
You can use below code. I have assumed the structure of input dict to be same throughout the execution.
# Input List
my_list = ['string1~', 'string2~', 'string3~', 'string4~', 'string5~', 'string6~']
# Input Dict
# Removed duplicate key "B" from the dict
my_iterable = {'A':'xyz',
'B':'string6',
'C':[{'B':'string4', 'D':'123'}],
'E':[{'F':'321', 'B':'string1'}],
'G':'jkl',
}
# setting search key
search_key = "B"
# Main code
for i, v in my_iterable.items():
if i == search_key:
if not isinstance(v,list):
search_in_list = [i for i in my_list if v in i]
if search_in_list:
my_iterable[i] = search_in_list[0]
else:
try:
for j, k in v[0].items():
if j == search_key:
search_in_list = [l for l in my_list if k in l]
if search_in_list:
v[0][j] = search_in_list[0]
except:
continue
# print output
print (my_iterable)
# Result -> {'A': 'xyz', 'B': 'string6~', 'C': [{'B': 'string4~', 'D': '123'}], 'E': [{'F': '321', 'B': 'string1~'}], 'G': 'jkl'}
Above can has scope of optimization using list comprehension or using
a function
I hope this helps and counts!
In some cases, if your nesting is kind of complex you can treat the dictionary like a json string and do all sorts of replacements. Its probably not what people would call very pythonic, but gives you a little more flexibility.
import re, json
my_list = ['string1~', 'string2~', 'string3~', 'string4~', 'string5~', 'string6~']
my_iterable = {'A':'xyz',
'B':'string6',
'C':[{'B':'string4', 'D':'123'}],
'E':[{'F':'321', 'B':'string1'}],
'G':'jkl'}
json_str = json.dumps(my_iterable, ensure_ascii=False)
for val in my_list:
json_str = re.sub(re.compile(f"""("[B]":\\W?")({val[:-1]})(")"""), r"\1" + val + r"\3", json_str)
my_iterable = json.loads(json_str)
print(my_iterable)

Reversing the key values in a dictionary (advanced reverse string in Python)

So what I was trying to do was output the string "33k22k11k", which is just the last value followed by the reversed last key followed by the second last value followed by the second last reversed key and so on. I'm not sure how to get the reversed key value for the specific loop that I am in. From the code I currently I have, I get the output:
dict = {"k1":1, "k2":2, "k3":3}
current=""
current_k=""
for k,v in dict.items():
for i in k:
current_k=i+current_k
current=str(v)+current_k+current
print(current)
print(current_k)
33k2k1k22k1k11k
3k2k1k
Edited
First of all, if you are on python < 3.6, dict does not keep the order of items. You might want to use collections.OrderedDict for your purpose.
d = {"k1":1, "k2":2, "k3":3}
d.keys()
# dict_keys(['k2', 'k1', 'k3'])
whereas,
d = OrderedDict()
d['k1'] = 1
d['k2'] = 2
d['k3'] = 3
d.keys()
# odict_keys(['k1', 'k2', 'k3'])
With our new d, you can either add the key and values and reverse it:
res = ''
for k, v in d.items():
res += str(k) + str(v)
res[::-1]
# '33k22k11k'
or reversely iterate:
res = ''
for k, v in reversed(d.items()):
res += str(v)[::-1] + str(k)[::-1]
res
# '33k22k11k'
I may be wrong but it seems like you would want to reset the value of current_k each time you access a new key
dict = {"k1":1, "k2":2, "k3":3}
current=""
for k,v in dict.items():
current_k=""
for i in k:
current_k=i+current_k
current=str(v)+current_k+current
print(current)
print(current_k)
Why not simply do:
print(''.join([a+str(b) for a,b in dict.items()])[::-1])
Output:
"33k22k11k"
But if the values are different from the keys, do:
print(''.join([str(b)[::-1]+a for a,b in dict.items()[::-1]]))
You can use the Python map function to create the reversed string(using f-string) for each key/value pair and then join it.
dict1 = {"k1":1, "k2":2, "k3":3}
new_dict = "".join(map(lambda k, v: f'{k}{v}'[::-1] , dict1.keys(), dict1.values()))
Output:
33k22k11k
You can do something like this perhaps:
dict = {"k1":1, "k2":2, "k3":3}
print("".join(list(reversed([str(v)+"".join(reversed(k)) for k, v in dict.items()]))))
Output:
33k22k11k

Find non-Empty value in dict

I have a dict like this:
d = {'first':'', 'second':'', 'third':'value', 'fourth':''}
and I want to find first non-empty value (and it's name, in this example 'third'). There may be more than one non-empty value, but I only want the first one I find.
How can I do this?
Use an OrderedDict which preserves the order of elements. Then loop over them and find the first that isn't empty:
from collections import OrderedDict
d = OrderedDict()
# fill d
for key, value in d.items():
if value:
print(key, " is not empty!")
You could use next (dictionaries are unordered - this somewhat changed in Python 3.6 but that's only an implementation detail currently) to get one "not-empty" key-value pair:
>>> next((k, v) for k, v in d.items() if v)
('third', 'value')
Like this?
def none_empty_finder(dict):
for e in dict:
if dict[e] != '':
return [e,dict[e]]
d = {'first':'', 'second':'', 'third':'value', 'fourth':''}
for k, v in d.items():
if v!='':
return k, v
Edit 1
from the comment if the value is None or '' we better use if v: instead of if v!=''. if v!='' only check the '' and skip others
You can find empty elements and make a list of them:
non_empty_list = [(k,v) for k,v in a.items() if v]
By using list comprehension, you can list all the non-empty values and then fetch the 0th value:
[val for key, val in d.items() if val][0]

I have list which have keys of dictionary. How to access the dictionary using these keys dynamically

I have list which have keys of dictionary. How to access the dictionary using these keys dynamically. e.g
key_store = ['test','test1']
mydict = {"test":{'test1':"value"},"test3":"value"}
So how to access mydict using key_store I want to access mydict['test']['test1'].
Note: key_store store depth of keyword means it have keywords only its value will be dictionary like test have dictionary so it have 'test','test1'
You can do this with a simple for-loop.
def get_nested_key(keypath, nested_dict):
d = nested_dict
for key in keypath:
d = d[keypath]
return d
>>> get_nested_key(('test', 'test1'), Dict)
Add error checking as required.
Use recursion:
def get_value(d, k, i):
if not isinstance(d[k[i]], dict):
return d[k[i]]
return get_value(d[k[i]], k, i+1)
The parameters are the dictionary, the list and an index you'll be running on.
The stop condition is simple; Once the value is not a dictionary, you want to return it, otherwise you continue to travel on the dictionary with the next element in the list.
>>> key_store = ['test','test1']
>>> Dict = {"test":{'test1':"value"},"test3":"value"}
>>> def get_value(d, k, i):
... if isinstance(d[k[i]], str):
... return d[k[i]]
... return get_value(d[k[i]], k, i+1)
...
>>> get_value(Dict, key_store, 0)
'value'
You could do this with a simple dictionary reduce:
>>> mydict = {"test": {'test1': "value"}, "test3": "value"}
>>> print reduce(dict.get, ['test', 'test1'], mydict)
value

How do I loop over a map object's 2ND value in reverse?

I have a map and I am using a for loop to loop through it, but it only gives me the first value,.in ascending order. Any help appreciated!
What do you mean by ascending order? You could sort them:
Python 2.7
for v in sorted(map.itervalues()):
print v
Python 3
for v in sorted(map.values()):
print v
If instead you wanted keys to be sorted, try:
for _, v in sorted(map.items()):
print v
I'm assuming that by map you mean a dict, by "first value" you mean key, and by "second value" you mean value?
You can use .iteritems() to iterate over key/value pairs instead of just keys
d = {'a': 1, 'b': 2}
for k, v in d.iteritems():
print "key:", k
print "value:", v
Or .itervalues() to iterate over just the values:
for v in d.valueitems():
print "value:", v
There's no way to apply ordering to a dict's items directly -- iteration over a dict can occur in any order. You can however iterate over a sorted iteritems():
for k, v in sorted(d.iteritems(), key=lambda k, v: v):
print "key:", k
print "value:", v
Or just the values:
for v in sorted(d.valueitems()):
print "value:", v
It's not very clear what your asking, but I took my best guess. Next time try to post some of your already done code!
>>> mydict = {'a': 1, 'b': 89, 'c': 56, 'd': 9123, 'e':-23}
>>> mylist = []
>>> for k in mydict:
... mylist.append(mydict[k])
...
>>> mylist.sort()
>>> mylist.reverse()
>>> mylist
[9123, 89, 56, 1, -23]
>>>
Explanation:
Lines
Set mydict to some value of numbers (because you want it in reverse)
Set mylist to a blank list
Loop over the first value of the dict
Use the first value to get the second value with mydict[k].
Append that to mylist
Use mylist.sort() to sort in ascending order
Use mylist.reverse() to reverse it.

Categories

Resources