I have the following python dictionary (a dict in a dict):
d = {'k1': {'kk1':'v1','kk2':'v2','kk3':'v3'},'k2':{'kk1':'v4'}}
I can't get my brains to figure out the list comprehension to get a list of all values (v1, v2...). If you can give my an example with a lambda also, that you be nice.
The goal is to have values_lst = ['v1','v2','v3','v4']
Thanks
Combine two loops to "flatten" the dict of dicts. Loop over the values of d first, and then loop over the values of the values of d. The syntax might be a bit hard to grasp at first:
values_lst = [v for x in d.values() for v in x.values()]
Related
I have a code snippit that groups together equal keys from a list of dicts and adds the dict with equal ObjectID to a list under that key.
Code bellow works, but I am trying to convert it to a Dictionary comprehension
group togheter subblocks if they have equal ObjectID
output = {}
subblkDBF : list[dict]
for row in subblkDBF:
if row["OBJECTID"] not in output:
output[row["OBJECTID"]] = []
output[row["OBJECTID"]].append(row)
Using a comprehension is possible, but likely inefficient in this case, since you need to (a) check if a key is in the dictionary at every iteration, and (b) append to, rather than set the value. You can, however, eliminate some of the boilerplate using collections.defaultdict:
output = defaultdict(list)
for row in subblkDBF:
output[row['OBJECTID']].append(row)
The problem with using a comprehension is that if really want a one-liner, you have to nest a list comprehension that traverses the entire list multiple times (once for each key):
{k: [d for d in subblkDBF if d['OBJECTID'] == k] for k in set(d['OBJECTID'] for d in subblkDBF)}
Iterating over subblkDBF in both the inner and outer loop leads to O(n^2) complexity, which is pointless, especially given how illegible the result is.
As the other answer shows, these problems go away if you're willing to sort the list first, or better yet, if it is already sorted.
If rows are sorted by Object ID (or all rows with equal Object ID are at least next to each other, no matter the overall order of those IDs) you could write a neat dict comprehension using itertools.groupby:
from itertools import groupby
from operator import itemgetter
output = {k: list(g) for k, g in groupby(subblkDBF, key=itemgetter("OBJECTID"))}
However, if this is not the case, you'd have to sort by the same key first, making this a lot less neat, and less efficient than above or the loop (O(nlogn) instead of O(n)).
key = itemgetter("OBJECTID")
output = {k: list(g) for k, g in groupby(sorted(subblkDBF, key=key), key=key)}
You can adding an else block to safe on time n slightly improve perfomrance a little:
output = {}
subblkDBF : list[dict]
for row in subblkDBF:
if row["OBJECTID"] not in output:
output[row["OBJECTID"]] = [row]
else:
output[row["OBJECTID"]].append(row)
I am dealing with a dictionary that is formatted as such:
dic = {'Start': [['Story' , '.']],
'Wonderful': [('thing1',), ["thing1", "and", "thing2"]],
'Amazing': [["The", "thing", "action", "the", "thing"]],
'Fantastic': [['loved'], ['ate'], ['messaged']],
'Example': [['bus'], ['car'], ['truck'], ['pickup']]}
if you notice, in the story key, there is a tuple within a list. I am looking for a way to convert all tuples within the inner lists of each key into lists.
I have tried the following:
for value in dic.values():
for inner in value:
inner = list(inner)
but that does not work and I don't see why. I also tried an if type(inner) = tuple statement to try and convert it only if its a tuple but that is not working either... Any help would be very greatly appreciated.
edit: I am not allowed to import, and only have really learned a basic level of python. A solution that I could understand with that in mind is preferred.
You need to invest some time learning how assignment in Python works.
inner = list(inner) constructs a list (right hand side), then binds the name inner to that new list and then... you do nothing with it.
Fixing your code:
for k, vs in dic.items():
dic[k] = [list(x) if isinstance(x, tuple) else x for x in vs]
You need to update the element by its index
for curr in dic.values():
for i, v in enumerate(curr):
if isinstance(v, tuple):
curr[i] = list(v)
print(dic)
Your title, data and code suggest that you only have tuples and lists there and are willing to run list() on all of them, so here's a short way to convert them all to lists and assign them back into the outer lists (which is what you were missing) (Try it online!):
for value in dic.values():
value[:] = map(list, value)
And a fun way (Try it online!):
for value in dic.values():
for i, [*value[i]] in enumerate(value):
pass
I have two lists of tuples
keys = [(0,1), (2,1)]
values = [('a','b'), ('c','d')]
I want to make a dictionary dict that will apply a function f1 to each
dict.keys[i] = keys[i][0], keys[i][i]: f1(keys[i][0],keys[i][1])
And for the values of the dictionary, I would like to be tuples
dict.values[i] = (f2(values[i][0]), f2(values[i][1]))
What is the most efficient way of doing that in one pass in a pythonic way?
You can do this using a dictionary comprehension.
out = {f1(keys[i][0], keys[i][1]):(f2(values[i][0]),f2(values[i][1])) for i in range(len(keys))}
If you want to avoid using range you can also use zip to accomplish the same thing:
out = {f1(M[0],M[1]):(f2(N[0]), f2(N[1])) for M,N in zip(keys, values)}
And even briefer
out = {f1(*M):tuple(map(f2, N)) for M,N in zip(keys, values)}
If you're on Python 2.6 or earlier (prior to dictionary comprehensions), you can always use a list comprehension and convert to a dictionary explicitly.
out = dict([(f1(*M), tuple(map(f2, N))) for M,N in zip(keys, values)])
I have a dictionary of lists, each list greater than 50 items, and to simplify, lets say the dictionary keys are ['a','b','c']. I spend way to long trying to figure out a very pythonic was to sort and slice these lists. What I have so far:
dict = dictionary_of_lists under discussion
[dict[k].sort(reverse=True) for k in dict.keys()]
for k, l in dict.items():
slice = 10 if k in ('a','c') else 20
dict[k] = l[:slice]
I end up with a sorted, and trimmed up list, just like I want. But what I wanted was a one line piece of code like [dict[k].sort(reverse=True) for k in dict.keys()] when I slice against the sorted list. And if someone can figure out how to put the sorting and slicing together, they would be my hero.
UPDATE: First, I like being able to ask somewhat complex questions because they help me learn better coding skills (since I am self taught). So thanks everyone below! My new code:
for c in list_of_categories:
list = [getattr(p,c.name) for p in people if hasattr(p,c.name)]
slice = c.get_slice_value # I added an #property function to a class named `Category`
c.total = sum(sorted(list, reverse=True)[:slice])
List comprehensions with side effects are usually considered bad style. Create a new dict instead:
dct = {k: sorted(l, reverse=True)[:10 if k in ('a','c') else 20]
for k, l in dct.items()}
Also slice values look arbitrary at the moment, it might be better to configure them separately, for example:
slices = {
'a': 10,
'b': 10,
'c': 20
}
dct = {k: sorted(l, reverse=True)[:slices[k]]
for k, l in dct.items()}
sort() works in place, affecting each list. You'd want to create new ones:
[sorted(d[k], reverse = True)[:10 if k in ('a','c') else 20] for k in d.keys()]
Note that it's not very readable.
I wrote the below code working with dictionary and list:
d = computeRanks() # dictionary of id : interestRank pairs
lst = list(d) # tuples (id, interestRank)
interestingIds = []
for i in range(20): # choice randomly 20 highly ranked ids
choice = randomWeightedChoice(d.values()) # returns random index from list
interestingIds.append(lst[choice][0])
There seems to be possible error because I'm not sure if there is a correspondence between indices in lst and d.values().
Do you know how to write this better?
One of the policies of dict is that the results of dict.keys() and dict.values() will correspond so long as the contents of the dictionary are not modified.
As #Ignacio says, the index choice does correspond to the intended element of lst, so your code's logic is correct. But your code should be much simpler: d already contains IDs for the elements, so rewrite randomWeightedChoice to take a dictionary and return an ID.
Perhaps it will help you to know that you can iterate over a dictionary's key-value pairs with d.items():
for k, v in d.items():
etc.