flornquake
flornquake

Reputation: 3396

Union of many Counters

What's the best way (in terms of readability and efficiency) of finding the union of a list of Counters?

For example, my list might look like this:

counters = [Counter({'a': 6, 'b': 3, 'c': 1}),
            Counter({'a': 2, 'b': 5}),
            Counter({'a': 4, 'b': 4}),
            ...]

I want to calculate the union, i.e. counters[0] | counters[1] | counters[2] | ....

One way of doing it would be this:

def counter_union(iterable):
    return functools.reduce(operator.or_, iterable, Counter())

Is there a better approach?

Upvotes: 4

Views: 2771

Answers (3)

flornquake
flornquake

Reputation: 3396

user2357112 and Tim's comments made me realize that if you are going to use reduce, you should at least use operator.ior, not operator.or_. In Python 3.3+, this will avoid creating a new counter for every iteration.

def counter_union(iterable):
    return functools.reduce(operator.ior, iterable, Counter())

Upvotes: 3

Tim Peters
Tim Peters

Reputation: 70735

Goodness, when did Python programmers become afraid of easy loops? LOL.

result = Counter()
for c in counters:
    result |= c

There really aren't prizes in real life for squashing things into as few characters as theoretically possible. Well, ya, there are in Perl, but not in Python ;-)

Later: pursuant to user2357112's comment, starting with Python 3.3 the code above will do "in place" unions into result. That is, result is truly reused, possibly growing larger on each iteration.

In any spelling of

counters[0] | counters[1] | counters[2] | ...

instead, the entire partial result so far keeps getting thrown away when the next partial result is computed. That may - or may not - be a lot slower.

Upvotes: 7

jterrace
jterrace

Reputation: 67138

I think a loop is much more readable:

def counter_union(iterable):
    union = Counter()
    for counter in counters:
        union |= counter
    return union

Upvotes: 2

Related Questions