1

I try to put the values in the list to the dictionary, and count how many times they appear in that list, however the second way doesn't work. Does anyone know the reason?

#one way
list1=['1','2','2','3','2','2']
dict1={}
for keys in list1:
  dict1[keys]=0
for keys in list1:
  dict1[keys]=dict1[keys]+1
print('dict1 is',dict1)

#second way
list2=list1
dict2={}
for keys in list2:
  dict2[keys]+=1
print('dict2 is',dict2)
Georgy
  • 12,464
  • 7
  • 65
  • 73
M Z
  • 13
  • 3
  • `list2=list1` is a common beginner error - its just another name for the same data - see https://stackoverflow.com/questions/2612802/how-to-clone-or-copy-a-list ... it is a NullOp that does nothing for your code# – Patrick Artner Jan 15 '19 at 18:50
  • yea you are right. Actually, I just wanna account the list's values in two way. However, there is a mistake in the second way, but I do not know how to fix that, the first way can get the dict such as{'1':1,'2':4,'3':1}, but I think that the code is so complex, – M Z Jan 15 '19 at 18:58
  • 1
    Possible duplicate of [Best way to handle a keyerror in a dict in python](https://stackoverflow.com/questions/36597869/best-way-to-handle-a-keyerror-in-a-dict-in-python) – Georgy Jan 15 '19 at 19:10
  • 1
    There's actually a class purpose built for this task in the `collections` module: [`Counter`](https://docs.python.org/3/library/collections.html#collections.Counter). `Counter(list1)` is `Counter({'2': 4, '1': 1, '3': 1})`, and can be treated as a dict. – Patrick Haugh Jan 15 '19 at 19:19

1 Answers1

3

Way 2 does not work because the keys do not exists yet. You can not use += for values on non existent keys - only on keys that contain (f.e.) integers.


Collection of alternate ways:

O(n**2) - solution:

list1=['1','2','2','3','2','2']

# the worst petrformance I can imagine:
worst = {}
for item in list1:
    worst[item] = list1.count(item)

print(worst) # {'1': 1, '3': 1, '2': 4} 

Worst because: list.count() iterates the whole list to count one elements occurences. It touches this list 6 times for every number in it. It counts 2 four times (just to make sure) and reassignes the counted value to the key of over and over.

It is a O(n**2) approach. You can "optimize" it by only looping over the set(list1) which reduces it to counting only each unique number (3*6 instead of 6*6) - but the problem has several O(n) solution == one pass over the list.

Your solution is O(2*n) - iterating the list twice to create zero-indexes and then count them.

O(n) - solutions (that differ in performance):

# zeroth'st way: check and test and create if needed else add
ddd = {}
for item in list1:   # touches each item exactly once
    if item in ddd:       # but uses additional checks and conditions 
        ddd[item] += 1
    else:
        ddd[item] = 1

print(ddd) 

#second way   # slowish - but better then two for loops (kinda your one way optimized)
dict2={}
for key in list1:             # touches each element once, no checks but
    dict2.setdefault(key,0)   # setdefault + assignment is slower then defaultdict
    dict2[key]+=1

print('dict2 is',dict2)

# third way     # faster then setdefault due to  optimizations in defaultdict
from collections import defaultdict

d = defaultdict(int)
for key in list1:
    d[key]+=1

print("defaultdict is", d)

# fourth way     # slower because it needs additional list sorting to work
from itertools import groupby

dd = { k:len(list(v)) for k,v in groupby(sorted(list1))} #needs sorted list here

print("groupby is", dd)

# fifth way using Counter
from collections import Counter 

print(     Counter(list1))   
print(dict(Counter(list1)))  

Output's:

{'1': 1, '3': 1, '2': 4} 
dict2 is {'1': 1, '2': 4, '3': 1}
defaultdict is defaultdict(<class 'int'>, {'1': 1, '2': 4, '3': 1})
groupby is {'1': 1, '2': 4, '3': 1}
Counter({'2': 4, '1': 1, '3': 1})
{'1': 1, '3': 1, '2': 4}    

Doku:

with Counter and defaultdict being the fastisht methods - you would have to measure to get a "winner".

Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
  • 1
    It seems most idiomatic (if not most efficient) is `collections.Counter(list1)`. – jpp Jan 15 '19 at 23:30