4

I want to split a dictionary in two based on whether any of an array of strings is present in a property within the main dictionary. Currently I can achieve this with two separate dictionary comprehensions (below), but is there a more efficient way to do this with only one line/dictionary comprehension?

included = {k:v for k,v in users.items() if not any(x.lower() in v["name"].lower() for x in EXCLUDED_USERS)}
excluded = {k:v for k,v in users.items() if any(x.lower() in v["name"].lower() for x in EXCLUDED_USERS)}

EDIT

EXCLUDED_USERS contains a list of patterns.

ryansin
  • 1,735
  • 2
  • 26
  • 49

2 Answers2

5

This solution is more verbose, but it should be more efficient and possibly more readable:

included = {}
excluded = {}

lower_excluded_users = [x.lower() for x in EXCLUDED_USERS]

for k,v in users.items():
    if any(x in v["name"].lower() for x in lower_excluded_users):
        excluded[k] = v
    else:
        included[k] = v

I don't think it can be done with one single comprehension. It's possible to use a ternary operator inside the k:v statement, it's not possible to use else after the if in a {k:v for k,v in users.items() if k ...} pattern.

Eric Duminil
  • 52,989
  • 9
  • 71
  • 124
0

One line solution (with lower_excluded_users which I couldn't resist making)

included, excluded = dict(), dict()

# ssly, you don't have to do this everytime
lower_excluded_users = [x.lower() for x in EXCLUDED_USERS]

# and now the one-line answer using if-else-for construct with
# v substituted by D[k]. And instead of using `for k, v in dicn.items()`
# I have used [... for aKey in dicn.keys()]

[ excluded.update({aKey: users[aKey]}) \
    if any(x in users[aKey]["name"].lower() for x in lower_excluded_users) \
    else \
    included.update({aKey: users[aKey]}) \
    for aKey in users.keys()
]

Or one without beautification:

[excluded.update({aKey: users[aKey]}) if any(x in users[aKey]["name"].lower() for x in lower_excluded_users) else included.update({aKey: users[aKey]}) for aKey in users.keys()]
RinkyPinku
  • 410
  • 3
  • 20
  • 1
    You only use a list comprehension as a for loop, without caring for the result of the comprehension. It's less readable and doesn't bring any advantage IMHO. Note : I didn't downvote. – Eric Duminil Jul 13 '17 at 11:37
  • @EricDuminil - actually, calling a function within a list/dict comprehension block would be marginally faster (<5%) at the expense of memory (around 8MB for each 1M loop provided that the function returns `None`). It will also make your code notoriously hard to read and debug. – zwer Jul 13 '17 at 11:49
  • 1
    It's not about speed or readability, OP expects a one liner. That's what I give. Everyone knows what makes Python a darling language – RinkyPinku Jul 13 '17 at 12:06
  • He's right, I said a one liner and this accomplishes that – ryansin Jul 13 '17 at 14:55
  • @Sinfieldd :: Umm... She? – RinkyPinku Jul 13 '17 at 16:18
  • Humblest apologies ☺ – ryansin Jul 13 '17 at 16:27