-2
counts = defaultdict(int)
for elem in sets:         #list of sets
    for _ in elem:        #each set contains elements that may be duplicates among the sets
        counts[_] += 1

Is there a way to use dict comprehension for code like this?

sunnyata
  • 1
  • 1
  • 3
    give sample input and output – I'mahdi Sep 25 '21 at 10:37
  • 5
    Could you explain why you want to use a list-comp for this? The title of your post and the body of the post are somewhat contradictory. A list-comp results in a `list` - what you're doing is wanting a `dict` it seems... – Jon Clements Sep 25 '21 at 10:52
  • 1
    IndentationError - is that your problem? [mre], [ask] – Patrick Artner Sep 25 '21 at 12:10
  • 3
    Generally you do not want to use a list comprehension to get "side-effects" - use them if you want a list in result. For anything else - use a loop - not side effects of creating a list that you do not want. – Patrick Artner Sep 25 '21 at 12:11
  • 1
    Beside the point, but the conventional use of `_` as a variable name is [for a throwaway](/q/5893163/4518341), so using it here is confusing. – wjandrea Sep 25 '21 at 18:53
  • 1
    BTW, welcome to Stack Overflow! Check out the [tour], and [ask] if you want tips. – wjandrea Sep 25 '21 at 18:55
  • @JonClements yes, in this particular excercise the counts were required to be stored in a dict. I probably meant dict comprehension, but mistyped :) these concepts still sound similar to me. – sunnyata Sep 27 '21 at 12:28

2 Answers2

0

Don't use a list comprehension for side effects (i.e. updating counts).

Instead, if you replace defaultdict(int) with Counter, you can write the solution in one line. (I assume your end goal is to make your code shorter/cleaner.)

sets = [
    {1, 2, 3},
    {1, 2},
    {3},
    {3}]
# ---
from collections import Counter
counts = Counter(x for s in sets for x in s)
print(counts)  # -> Counter({3: 3, 1: 2, 2: 2})
  • Note: Instead of the nested generator expression, you might prefer to use itertools.chain.from_iterable(sets).
wjandrea
  • 28,235
  • 9
  • 60
  • 81
  • Yes, the goal was indeed to see if this was something that could be accomplished in one line, as it looks like a fairly simple iteration process. I was thinking that maybe the walrus operator or something else that I am unfamiliar with may work in cases like this. Thanks for pointing out the Counter collection! – sunnyata Sep 27 '21 at 12:26
0

If you are adamant at using list comprehension, you can do this way:

>>> counts = {1:4, 2:5, 3:8, 4:2, 5:9}
>>> sets = [{2,3}, {3,1}, {2}]
>>> [[counts.update({e: counts[e]+1}) for e in si] for si in sets]
[[None, None], [None, None], [None]]
>>> counts
{1: 5, 2: 7, 3: 10, 4: 2, 5: 9}

But yes, as others suggested, you can do it using Counter:

#1: use Counter to get new counts from sets

#2: merge both new & default counters

#3: get Counter back into your dictionary

With sample input:

>>> counts = {1:4, 2:5, 3:8, 4:2, 5:9}
>>> sets = [{2,3}, {3,1}, {2}]
>>> from collections import Counter
>>> cntr_new = Counter(x for s in sets for x in s)
>>> cntr_new
Counter({2: 2, 3: 2, 1: 1})
>>> cntr_orig = Counter(counts)
>>> cntr_final = cntr_orig + cntr_new

Final result:

>>> dict(cntr_final)
{1: 5, 2: 7, 3: 10, 4: 2, 5: 9}
wjandrea
  • 28,235
  • 9
  • 60
  • 81
KiranM
  • 1,306
  • 1
  • 11
  • 20
  • That list `[[None, None], [None, None], [None]]` is pointless. That's why we say, [don't use a list comprehension for side effects](https://stackoverflow.com/a/5753614/4518341). Use a plain for-loop instead. And that'd be a perfectly valid answer! To quote from [answer], *'The answer can be “don’t do that”, but it should also include “try this instead”.'* – wjandrea Sep 26 '21 at 22:48