简体   繁体   中英

Count equal tuple-elements of a list in Python3

The code here works for me. But I am new to Python and want to know and learn if there is a more elegant or pythonic way to do that job.

There is a list of two-element-tuples. I want to join equal list elements and store the count of equal elements as a third tuple-element (the first in front of the other two-tuple elements).

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

org = [ ( 12, 4 ),
        (  8, 4 ),
        ( 12, 8 ),
        ( 12, 8 ) ]

# should result in
# [ ( 1, 12, 4 ),
#   ( 1,  8, 4 ),
#   ( 2, 12, 8 ) ]


def count_element(count_in, e):
    """
        How often does 'e' appear in 'count_in'.
    """
    count = 0
    for x in count_in:
        if x == e:
            count += 1
    return count


def has_element(look_in, e):
    """
        'look_in' is a three-element tuple
        'e' is a two-element tuple
    """ 
    for x, y, z in look_in:
        if y == e[0] and z == e[1]:
            return True
    return False


def main():
    result = []

    for i in org:
        if has_element(result, i):
            continue
        c = count_element(org, i)
        resi = (c, i[0], i[1])
        result += [resi]

    print(org)
    print(result)


if __name__ == '__main__':
    main()

Similar to the other answers, but for any tuple dimension:

org = [(12, 4), (8, 4), (12, 8), (12, 8), (4, 3, 2, 1)]

from collections import Counter
[(count,) + item for item, count in Counter(org).items()]
# [(2, 12, 8), (1, 12, 4), (1, 4, 3, 2, 1), (1, 8, 4)]

Counter is definitely very useful (and idiomatic) for this, but it's good to remember that it's easy to construct a similar structure with a plain dict :

counter = dict()
for item in org:
    if item not in counter:
        counter[item] = 1
    else:
        counter[item] += 1 
    # Alternatively, just: counter[item] = counter.get(item, 0) + 1      

Its properties are ideal for this task. If you're not familiar with dict s, more amazement is awaiting you. :)

Using a combination of a Counter and a list comprehension, we can do this fairly quickly. It will result in a new tuple though, since a tuple is immutable.

from collections import Counter

org = [ ( 12, 4 ),
        (  8, 4 ),
        ( 12, 8 ),
        ( 12, 8 ) ] 
counts = Counter(org)
org_counts = [(counts[o], o[0], o[1]) for o in set(org)]

The org_count variable looks like this at the end of the script:

[(2, 12, 8), (1, 12, 4), (1, 8, 4)]

It's important to note that the Counter is a dictionary subclass, thus unordered. That means you final list can be in a different order than the original org variable. I made the assumption that this was alright, because duplicates would be compressed into a single entry, thus messing up the order.

In the list comprehension, I utilize the unique set of org to prevent duplicate entries.

for o in set(org)

An easier way to do what you are doing would be to use collections.Counter and list comprehension -

import collections
def main()
    result = [(v,) + k for k,v in collections.Counter(org).items()]
    print(org)
    print(result)

Please note, this would not preserve the order from the original list.

Demo -

>>> org = [ ( 12, 4 ),
...         (  8, 4 ),
...         ( 12, 8 ),
...         ( 12, 8 ) ]
>>>
>>> result = [(v,) + k for k,v in collections.Counter(org).items()]
>>> result
[(2, 12, 8), (1, 12, 4), (1, 8, 4)]

If you want to preserve the order, I would suggest you use a set to record the elements that are already seen and use collections.Counter() for counting. Example -

import collections
def main():
    result = []
    seen = set()
    counts = collections.Counter(org)
    for x in org:
        if x not in seen:
            result.append((counts[x],) + x)
            seen.add(x)

Demo -

>>> org
[(12, 4), (8, 4), (12, 8), (12, 8)]
>>>
>>> result = []
>>> seen = set()
>>> counts = collections.Counter(org)
>>> for x in org:
...     if x not in seen:
...         result.append((counts[x],) + x)
...         seen.add(x)
...
>>> result
[(1, 12, 4), (1, 8, 4), (2, 12, 8)]

Also, just a suggestion , a better way to do the following -

def has_element(look_in, e):
    """
        'look_in' is a three-element tuple
        'e' is a two-element tuple
    """ 
    for x, y, z in look_in:
        if y == e[0] and z == e[1]:
            return True
    return False

Is to use any() , Example -

def has_element(look_in, e):
    """
        'look_in' is a three-element tuple
        'e' is a two-element tuple
    """ 
    return any(y == e[0] and z == e[1] for _, y, z in look_in)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM