I am using Python 3.6. I wrote following code, it's a for loop:
Edit: I made a mistake when I wrote var l down, so I re-type it here. Thanks for #Ender Look !!
l = [['a','1'], ['a','2'], ['a','3']]
d = {}
for i in l:
d[i[0]] = d.get(i[0], '') + '\t' + i[1]
print (d)
So, the result is what I want:
{'a': '\t1\t2\t3'}
Then I refactor above code as comprehension:
dict2 = {}
dict2 = {i[0]: dict2.get(i[0], '') + '\t' + i[1] for i in l}
print(dict2)
I though they should return same output. But the dict2 is:
{'a': '\t3'}
I want to know what's the matter with my dict comprehension? Thanks a lot!
You don't necessarily need to use a comprehension here. You can make use of defaultdict from collections:
>>> from collections import defaultdict
>>> d = defaultdict(str)
>>> for li in l:
... d[li[0]] += f'\t{li[1]}'
...
>>> d
defaultdict(<class 'str'>, {'a': '\t1\t2'})
Your dictionary comprehension doesn't work because it's use .get on itself.
dict2 = {i[0]: dict2.get(i[0], '') + '\t' + i[1] for i in l}
Until the end of the whole dictionary comprehension, this new dictionary isn't assigned to your actual dict2 variable. So all the times your comprehension tries to retrieve the value from dict2.get(...) it always executes that function from the empty dictionary dict2 = {}.
Sadly, I don't know (and I don't think it exists) a way to use .get on a dictionary comprehension about itself, because the variable dict2 isn't updated on "real time" (it wait until the end the comprehension). Or at least that I have understood, my humble knowledge isn't perfect.
Yes, you can do it as a single dict comprehension.
{k: v
for d in [{}]
for k, v in [d.__setitem__(k, d.get(k, '') + '\t' + v) or d
for k, v in [['a','1'], ['a','2'], ['a','3']]][-1].items()}
You shouldn't.
Note how we had to create a reference to an inner dict d anyway, since your algorithm has to look things up in the dict as it's being constructed. What is the outer comprehension even for? And we're throwing away all but the last element of the inner list comp.
It's much clearer to use a normal for loop like this,
d = {}
for k, v in [['a','1'], ['a','2'], ['a','3']]:
d[k] = d.get(k, '') + '\t' + v
Use the right tool for the job.
Related
Say you have a dict my_dict where all its elements are dictionaries.
What is the fastest way to iterate over all elements and change only 1 element of the encapsulated dict ?
basically this but faster:
for sub_dict in my_dict:
sub_dict["key_2"] = sub_dict["key_2"][0]
Note: I would be interested if anyone points out a way to do this with lambda functions
So, I rewrote your example (as it not kinda working) and compared with my dict comprehension
import datetime
my_dict = {i: [i ** 2] for i in range(1000000)}
now = datetime.datetime.now()
for sub_dict in my_dict:
my_dict[sub_dict] = my_dict[sub_dict][0]
print("First", datetime.datetime.now() - now) # First 0:00:00.088934
my_dict = {i: [i ** 2] for i in range(100000)}
now = datetime.datetime.now()
my_dict = {k: v[0] for k, v in my_dict.items()} # Second 0:00:00.007972
print("Second", datetime.datetime.now() - now)
This works, should be faster than the for loop. Not sure if it's optimal
[dict(sub_dict,key_2=sub_dict["key_2"][0])(sub_dict) for sub_dict in my_dict]
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)
I was playing around with a few solutions but am looking for an optimal and clean way to do this in Python. Basically I have a dictionary called 'd', and a list 'l'. If the dictionary contains a value not in the list delete that value in the dictionary. So given:
d = {1 : "bob", 2 : "mary", 3 : "joe"}
l = [2,3]
The first key of d gets deleted.
d = {2 : "mary", 3 : "joe"}
l = [2,3]
I have a working solution but feel the use of flags is unnecessary.
flag = False
del_vals = list()
for key in d.iterkeys():
for i in l:
if key == i: flag = True
if flag == False:
del_vals.append(key)
flag = False
for k in del_vals:
d.pop(k, None)
Is there a way I can write it to improve performance and make it more clean? Whether the use of generators or anything else is involved.
A list comprehension will work starting from Python 2.7:
d = {k: v for k, v in d.iteritems() if k in l}
Use items() instead of iteritems() in Python 3:
d = {k: v for k, v in d.items() if k in l}
You can also use sets for achieving the same:
z={i:d[i] for i in set(d.keys()).intersection(l)}
or use the below
p={i:d[i] for i in d.keys() if i in l}
or do this
q={i:d[i] for i in set(d.keys()) & set(l)}
Alternatively, you could do this as well:
for i in set(d.keys()) - set(l):
del(d[i])
I have a dictionary and I want to split it into two sub-dictioaries based on a string.
Is there a nicer (more pythonic) way to do it than this:
dict_1 = {k:v for (k,v) in initial_dict.iteritems() if string in k}
dict_2 = {k:v for (k,v) in initial_dict.iteritems() if string not in k}
dict_1 = {key:initial_dict.pop(key) for key in initial_dict if string in key}
dict_2 = initial_dict
I'll vote for your original. Clear, short, and only slightly inefficient
There is a way to do it without referencing or testing elements of initial_dict more than once, which is quite Pythonic if Pythonic means knowing that int(False)==0 and int(True)==1
dict1, dict2 = {}, {}
for k,v in initial_dict.items(): (dict2,dict1)[string in k][k] = v
Like I said, I prefer the questioner's way!
By the way, if you have to perform an n-way partition, dict_list[i][key]=v inside a loop that fetches or generates key,i and v starts to look a lot better than a multi-way if ... elif ... elif ...
I have many dictionaries like this:
dict1 = {1:[1,2,3],2:[2,3,4]}
dict2 = {2:[3,4,5],3:[4,5,6]}
I need to get
dict = {1:[1,2,3],2:[2,3,4,3,4,5],3:[4,5,6]}
# ^
# | order is unimportant
What is the best way of doing it?
Simple iteration an extending list...
for key, value in dict2.iteritems():
dict1.setdefault(key, []).extend(value)
Iterate through the keys of dict2; if the same key exists in dict1, concatenate the lists and set in dict1; otherwise just set in dict1
dict1 = {1:[1,2,3],2:[2,3,4]}
dict2 = {2:[3,4,5],3:[4,5,6]}
dicts = [dict1, dict2]
new_dict = {}
for d in dicts:
for k, v in d.iteritems():
if new_dict.has_key(k):
new_dict[k] = new_dict[k] + v
else:
new_dict[k] = v
a = {'a' : [1,2], 'b' : [3,4]}
b = {'a' : [3,4], 'b' : [1,2]}
for key in a.keys():
for elem in a[key]:
b[key].append(elem)
Oh, maybe there's some clever way to do it with reduce, but why not just write code like a normal person.
dict = {}
for each_dict in (dict1, dict2, ...): # ... is not real code
for key, value in each_dict:
if not dict.has_key(key):
dict[key] = []
dict[key] += value # list append operator
I have many dictionaries like this:
This way lets you "glue together" multiple dictionaries at a time:
dict(
(k, sum((d.get(k, []) for d in dicts), []))
for k in set(sum((d.keys() for d in dicts), []))
)