2

Is it possible to do something like this:

l = [1, 2, 2, 3, 4, 4, 1, 1]
d = {num: [num] if num not in d else d[num].append(num) for num in l}

Inherently, I wouldn't think so, without declaring d = {} first; even then, it doesn't append:

Output: {1: [1], 2: [2], 3: [3], 4: [4]}
# desired: {1: [1, 1, 1], 2: [2, 2], 3: [3], 4: [4, 4]}

Could use a defaultdict, curious if the comprehension is even possible?

jpp
  • 159,742
  • 34
  • 281
  • 339
pstatix
  • 3,611
  • 4
  • 18
  • 40

3 Answers3

3

d doesn't exist in your dictionary comprehension. Why not:

l = [1, 2, 2, 3, 4, 4, 1, 1]
d = {num: [num] * l.count(num) for num in set(l)}

EDIT: I think, it is better to use a loop there

d = {}
for item in l:
    d.setdefault(item, []).append(item)
djangoliv
  • 1,698
  • 14
  • 26
  • Yea I know it doesnt exist. When you declare `d = {}` prior, you get an overwritten (as I've shown) dict. It is unable to check for membership while iterating. – pstatix Apr 18 '18 at 14:49
  • the `d` you declare before is not the same as the one you are building, so you will never use the else statement. – djangoliv Apr 18 '18 at 14:56
  • 1
    Sorry, using `l.count` for each iteration is *very* inefficient. It's a full pass each time. And you are doing all this many times (3 times for 1) which is unnecessary. – jpp Apr 18 '18 at 14:57
  • @jpp I know, the best answer is to do a real loop. – djangoliv Apr 18 '18 at 14:58
  • You can at-least cast the list `l` to `set` while iteration! – Sohaib Farooqi Apr 18 '18 at 14:59
3

No, it's not possible. If you think about, it will make sense why. When Python evaluates an assignment statement, it first evaluates the right-hand side of the assignment - the expression. Since it hasn't evaluated the entire assignment yet, the variable on the left-hand hasn't been added to the current namespace yet. Thus, while the expression is being evaluated, the variable will be undefined.

As suggested, you can use collections.defaultdict to accomplish what you want:

>>> from collections import defaultdict
>>> 
>>> l = [1, 2, 2, 3, 4, 4, 1, 1]
>>> d = defaultdict(list)
>>> for num in l:
        d[num].append(num)


>>> d
defaultdict(<class 'list'>, {1: [1, 1, 1], 2: [2, 2], 3: [3], 4: [4, 4]})
>>> 
Christian Dean
  • 22,138
  • 7
  • 54
  • 87
1

No, you cannot refer to your list comprehension before the comprehension is assigned to a variable.

But you can use collections.Counter to limit those costly list.append calls.

from collections import Counter

l = [1, 2, 2, 3, 4, 4, 1, 1]

c = Counter(l)
d = {k: [k]*v for k, v in c.items()}

# {1: [1, 1, 1], 2: [2, 2], 3: [3], 4: [4, 4]}

Related: Create List of Single Item Repeated n Times in Python

jpp
  • 159,742
  • 34
  • 281
  • 339
  • This is radically different from your original answer (edited within the grace period), also why use a `Counter` over a `defaultdict`? This is exactly what `defaultdict` is for – Chris_Rands Apr 18 '18 at 15:11
  • To your first question, I actually *misread* the question, which is why I deleted it before updating (nothing sinister). To your second, this is a never-ending debate. One day, I'll write a Q&A explain all the benefits / disadvantages of each. In *this* instance, it's because I want to avoid repeated `list.append` calls. – jpp Apr 18 '18 at 15:17