optimizing nested dictionary comprehension - python-3.x

I have a dictionary comprehension that works, however I'm not sure if it is very readable/understandable (since I don't understand it well, but i'm a noob).
Data structures:
dict1 = {'this key 1':['this is a comment','this too','finally this'],
'this key 2':['this is a comment2','this too2','finally this2'],
'this key 3':['this is a comment3','this too3','finally this3']}
list1 = ['BlIb','vILB','vliH']
Desired output:
{'BlIb':{'this key 1':['this is a comment','this too','finally this']},
'vILB':{'this key 2':['this is a comment2','this too2','finally this2']},
'vliH':{'this key 3':['this is a comment3','this too3','finally this3']}}
Dictionary comprehensions:
As I said above, this one works, but I'm not sure how readable it is, or if I completely understand the inner dict comprehension (using 'key' twice)
all_data = {idx:{key:dict1[key]} for idx,key in zip(list1,dict1)}
Would this be the best way to do it? and if so could you explain this part{key:Dict[key]}?
And if not, how would you do it?
I've also tried the below, but just adds all three of the Dict items for each List item
all_data = {idx: {key:value for (key,value) in dict1.items()} for idx in list1}

Related

Iterating thru a not so ordinary Dictionary in python 3.x

Maybe it is ordinary issue regarding iterating thru a dict. Please find below imovel.txt file, whose content is as follows:
{'Andar': ['primeiro', 'segundo', 'terceiro'], 'Apto': ['101','201','301']}
As you can see this is not a ordinary dictionary, with a key value pair; but a key with a list as key and another list as value
My code is:
#/usr/bin/python
def load_dict_from_file():
f = open('../txt/imovel.txt','r')
data=f.read()
f.close()
return eval(data)
thisdict = load_dict_from_file()
for key,value in thisdict.items():
print(value)
and yields :
['primeiro', 'segundo', 'terceiro'] ['101', '201', '301']
I would like to print a key,value pair like
{'primeiro':'101, 'segundo':'201', 'terceiro':'301'}
Given such txt file above, is it possible?
You should use the builtin json module to parse but either way, you'll still have the same structure.
There are a few things you can do.
If you know both of the base key names('Andar' and 'Apto') you can do it as a one line dict comprehension by zipping the values together.
# what you'll get from the file
thisdict = {'Andar': ['primeiro', 'segundo', 'terceiro'], 'Apto': ['101','201','301']}
# One line dict comprehension
newdict = {key: value for key, value in zip(thisdict['Andar'], thisdict['Apto'])}
print(newdict)
If you don't know the names of the keys, you could call next on an iterator assuming they're the first 2 lists in your structure.
# what you'll get from the file
thisdict = {'Andar': ['primeiro', 'segundo', 'terceiro'], 'Apto': ['101','201','301']}
# create an iterator of the values since the keys are meaningless here
iterator = iter(thisdict.values())
# the first group of values are the keys
keys = next(iterator, None)
# and the second are the values
values = next(iterator, None)
# zip them together and have dict do the work for you
newdict = dict(zip(keys, values))
print(newdict)
As other folks have noted, that looks like JSON, and it'd probably be easier to parse it read through it as such. But if that's not an option for some reason, you can look through your dictionary this way if all of your lists at each key are the same length:
for i, res in enumerate(dict[list(dict)[0]]):
ith_values = [elem[i] for elem in dict.values()]
print(ith_values)
If they're all different lengths, then you'll need to put some logic to check for that and print a blank or do some error handling for looking past the end of the list.

Nested dictionary comprehension extracting one key value pair

I'm trying to get a single key value pair using dictionary comprehension as an exercise, I have accomplished this using for loops but the best I can do using dictionary comprehension returns an entire dictionary. If I use anything other than 'inner_key01' or 'inner_key02' in the if portion of the below code I get an empty dictionary.
I would like the code to return 'inner_value22'
my_dict = {'inner_key01' :{'inner_key1': 'inner_value1', 'inner_key2': 'inner_value2'},
'inner_key02' :{'inner_key21': 'inner_value21', 'inner_key22': 'inner_value22'}
}
next_dict = {inner_key: inner_value for inner_key, inner_value in my_dict.items() for outer_key, outer_value in my_dict.items()if inner_key == 'inner_key02'}
print(next_dict)

How to convert all dict key from str to float

I have got this current dictionary :
mydict = { "123.23":10.50, "45.22":53, "12":123 }
and I would to get this dictionary (with key as float):
mydict = { 123:23:10.50, 45.22:53, 12:123 }
I know that I can iterate over key and recreate a new dict like this:
new_dict = {}
for k in mydict.keys():
new_dict[float(k)]=mydict[k]
but I expect that it may be possible to convert dict key "inline" ( without to have to recreate a new dict ) ...
What is the most efficient method to do it ?
I suggest you to use a dictionary comprehension, which is easy to understand, as follows:
my_dict = { "123.23":10.50, "45.22":53, "12":123 }
my_dict = {float(i):j for i,j in mydict.items()}
print(my_dict) # {123.23: 10.5, 45.22: 53, 12.0: 123}
Use comprehension :
new_dict = { float(k): v for k, v in mydict.items() }
I expect that it may be possible to convert dict key "inline" ( without to have to recreate a new dict ) ...
What is the most efficient method to do it ?
Unless it materially matters to your runtime and you have time to waste profiling things and trying out various configurations, I'd strongly recommend just creating a second dict using a dict comprehension and focusing on actually relevant concerns: because dict views are "live" updating the dict as you iterate the keys directly may have odd side-effects e.g. you might find yourself iterating it twice as you first iterate the original keys, then try the keys you added; or the iteration might break entirely as deletions lead to internal storage compaction and the iterator gets invalidated.
So to change the key types without creating a new dict, you need to first copy the keys to a list, then iterate that and move values from one key to another:
for k in list(mydict.keys()):
mydict[float(k)] = mydict.pop(k)
However because of the deletions this may or may not be more efficient than creating a new dict with the proper layout, so the "optimisation" would be anything but.

Best way to exchange keys with values in a dictionary, where values are in a list?

My dict (cpc_docs) has a structure like
{
sym1:[app1, app2, app3],
sym2:[app1, app6, app56, app89],
sym3:[app3, app887]
}
My dict has 15K keys and they are unique strings. Values for each key are a list of app numbers and they can appear as values for more than one key.
I've looked here [Python: Best Way to Exchange Keys with Values in a Dictionary?, but since my value is a list, i get an error unhashable type: list
I've tried the following methods:
res = dict((v,k) for k,v in cpc_docs.items())
for x,y in cpc_docs.items():
res.setdefault(y,[]).append(x)
new_dict = dict (zip(cpc_docs.values(),cpc_docs.keys()))
None of these work of course since my values are lists.
I want each unique element from the value lists and all of its keys as a list.
Something like this:
{
app1:[sym1, sym2]
app2:[sym1]
app3:[sym1, sym3]
app6:[sym2]
app56:[sym2]
app89:[sym2]
app887:[sym3]
}
A bonus would be to order the new dict based on the len of each value list. So like:
{
app1:[sym1, sym2]
app3:[sym1, sym3]
app2:[sym1]
app6:[sym2]
app56:[sym2]
app89:[sym2]
app887:[sym3]
}
Your setdefault code is almost there, you just need an extra loop over the lists of values:
res = {}
for k, lst in cpc_docs.items():
for v in lst:
res.setdefault(v, []).append(k)
First create a list of key, value tuples
new_list=[]
for k,v in cpc_docs.items():
for i in range(len(v)):
new_list.append((k,v[i]))
Then for each tuple in the list, add the key if it isn't in the dict and append the
doc_cpc = defaultdict(set)
for tup in cpc_doc_list:
doc_cpc[tup[1]].add(tup[0])
Probably many better ways, but this works.

Remove values from dictionary

I have a large dictionary and I am trying to remove values from keys if they start with certain values. Below is a small example of the dictionary.
a_data = {'78567908': {'26.01.17', '02.03.24', '26.01.12', '04.03.03', '01.01.13', '02.03.01', '01.01.10', '26.01.21'}, '85789070': {'26.01.02', '09.01.04', '02.05.04', '02.03.17', '02.05.01'}, '87140110': {'03.15.25', '03.15.24', '03.15.19'}, '87142218': {'26.17.13', '02.11.01', '02.03.22'}, '87006826': {'28.01.03'}}
After I read in the dictionary, I want to remove values from all keys that start with '26.' or '02.' It is possible that would leave a key with no values (an empty set).
I do have code that works:
exclude = ('26.', '02.')
f_a_data = {}
for k, v in a_data.items():
f_a_data.setdefault(k,[])
for code in v:
print (k, code, not code.startswith(exclude))
if not code.startswith(exclude):
f_a_data[k].append(code)
print('Filtered dict:')
print(f_a_data)
This returns a filtered dict:
Filtered dict:
{'78567908': ['04.03.03', '01.01.13', '01.01.10'], '85789070': ['09.01.04'], '87140110': ['03.15.25', '03.15.24', '03.15.19'], '87142218': [], '87006826': ['28.01.03']}
Question 1: Is this the best way to filter a dictionary?
Question 2: How could i modify the above snippet to return values in a set like the original dict?
Your code is quite all right in complexity terms but can be "pythonized" a little and still remain readable.
My proposal: you can rebuild a dictionary using nested comprehensions and all to test if you should include the values:
a_data = {'78567908': {'26.01.17', '02.03.24', '26.01.12', '04.03.03', '01.01.13', '02.03.01', '01.01.10', '26.01.21'}, '85789070': {'26.01.02', '09.01.04', '02.05.04', '02.03.17', '02.05.01'}, '87140110': {'03.15.25', '03.15.24', '03.15.19'}, '87142218': {'26.17.13', '02.11.01', '02.03.22'}, '87006826': {'28.01.03'}}
exclude = ('26.', '02.')
new_data = {k:{x for x in v if all(s not in x for s in exclude)} for k,v in a_data.items()}
result:
>>> new_data
{'78567908': {'01.01.10', '01.01.13', '04.03.03'},
'85789070': {'09.01.04'},
'87006826': {'28.01.03'},
'87140110': {'03.15.19', '03.15.25', '03.15.24'},
'87142218': set()}
(here using a dictionary comprehension embedding a set comprehension (since you need a set) using a generator comprehension in all)

Resources