HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Count how many times values has appeared in list so far

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
appearedhowhastimesfarmanyvaluescountlist

Problem

I have a list of non-unique values. I want to output a list of the same length where each value corresponds to how many times that value has appeared so far.

The code I have written to do this seems over-complicated, so I think there has to be a better way.

x = [1,1,2,3,1,3]

def times_so_far(ls):

    out = [0]*len(x)
    counted = {}
    for i,v in enumerate(x):
        if v in counted:
            counted[v] += 1
        else:
            counted[v] = 0

        out[i] = counted[v]
    return out

times_so_far(x)
#[0, 1, 0, 0, 2, 1]

Solution

You can use a defaultdict, to remove the if v else.
To achieve this, you can do:

from collections import defaultdict
counted = defaultdict(int)
for i,v in enumerate(x):
    counted[v] += 1


To remove the need for out and enumerate you can yield on each iteration.

This would make your code very small and readable.

from collections import defaultdict

def times_so_far(list_):
    counted = defaultdict(int)
    for v in list_:
        counted[v] += 1
        yield counted[v]

print(list(times_so_far([1,1,2,3,1,3])))
#[0, 1, 0, 0, 2, 1]


It will return a generator object, hence why I use list.
If this is a problem then you can change yield to out.append() and re-add out.

This has \$O(n)\$ runtime, with a min \$O(n)\$ and maximum \$O(2n)\$ memory usage.

Code Snippets

from collections import defaultdict
counted = defaultdict(int)
for i,v in enumerate(x):
    counted[v] += 1
from collections import defaultdict

def times_so_far(list_):
    counted = defaultdict(int)
    for v in list_:
        counted[v] += 1
        yield counted[v]

print(list(times_so_far([1,1,2,3,1,3])))
#[0, 1, 0, 0, 2, 1]

Context

StackExchange Code Review Q#118290, answer score: 8

Revisions (0)

No revisions yet.