I have to create a list of blocked users per key. Each user has multiple attributes and if any of these attributes are in keys, the user is blocked.
I wrote the following nested for
-loop and it works for me, but I want to write it in a more pythonic way with fewer lines and more readable fashion. How can I do that?
for key in keys:
key.blocked_users = []
for user in get_users():
for attribute in user.attributes:
for key in keys:
if attribute.name == key.name:
key.blocked_users.append(user)
Aside from making it shorter, you could try to reduce the operations to functions that are optimized in Python. It may not be shorter but it could be faster then - and what's more pythonic than speed?. :)
For example you iterate over the keys for each attribute of each user. That just sreams to be optimized "away". For example you could collect the key-names in a dictionary (for the lookup) and a set (for the intersection with attribute names) once:
for key in keys:
key.blocked_users = []
keyname_map = {key.name: key.blocked_users for key in keys} # map the key name to blocked_user list
keynames = set(keyname_map)
The set(keyname_map)
is a very efficient operation so it doesn't matter much that you keep two collections around.
And then use set.intersection
to get the keynames that match an attribute name:
for user in get_users():
for key in keynames.intersection({attribute.name for attribute in user.attributes}):
keyname_map[key].append(user)
set.intersection
is pretty fast too.
However, this approach requires that your attribute.name
s and key.name
s are hashable.