如何处理有序列表中的每个项目,只在Python中重复一次?

时间:2013-04-04 19:32:06

标签: python python-2.7 set duplicate-removal test-and-set

我有一个有序的待处理列表,包括一些重复项,我只想处理第一次出现。现在,我在Python v2.7中这样做:

seen = set()
for (value, fmt) in formats:
  if fmt not in seen:
    seen.add(fmt)
    process(value, fmt)

是否有同时将新元素插入seen并检测它是否已存在? (这样可以避免在fmt中重复查找set。)

seen = set()
for (value, fmt) in formats:
  # myInsert() would return true if item was not already present.
  if seen.myInsert(fmt):
    process(value, fmt)

或者,或者,我可以以某种方式过滤我的formats以在循环之前排除重复的条目吗?

unique_formats = removeDuplicates(formats, key=itemgetter(1))
for (value, fmt) in unique_formats:
  process(value, fmt)

6 个答案:

答案 0 :(得分:2)

您可以在add()之前和之后获取集合的长度。如果它没有改变,格式已经在集合中。

seen = set()
for (value, fmt) in formats:
    l1 = len(seen)
    seen.add(fmt)
    if l1 != len(seen):
         process(value, fmt)

您的问题假定in测试是一项代价高昂的操作。事实证明并非如此。使用len()可能需要更多时间,尽管两者都非常快;

In [4]: seen = set(range(10000))

In [5]: %timeit 5995 in seen
10000000 loops, best of 3: 122 ns per loop

In [6]: %timeit len(seen)
1000000 loops, best of 3: 167 ns per loop

(在2.5 GHz Core Quad Q9300上使用CPython 2.7.3测量)

答案 1 :(得分:1)

我认为你的第一种方法是最好的。即使来自itertools recipesunique_everseen食谱也使用相同的方法。

def unique_everseen(iterable, key=None):
    "List unique elements, preserving order. Remember all elements ever seen."
    # unique_everseen('AAAABBBCCDAABBB') --> A B C D
    # unique_everseen('ABBCcAD', str.lower) --> A B C D
    seen = set()
    seen_add = seen.add
    if key is None:
        for element in ifilterfalse(seen.__contains__, iterable):
            seen_add(element)
            yield element
    else:
        for element in iterable:
            k = key(element)
            if k not in seen:
                seen_add(k)
                yield element

答案 2 :(得分:0)

你必须使用一套:

for (value, fmt) in set(formats):
   process(value, fmt)

答案 3 :(得分:0)

from ordereddict import OrderedDict
unique_formats = list(OrderedDict.fromkeys(format))
process(unique_formats)

这将保留订单并删除重复项

答案 4 :(得分:0)

您可以使用itertools.groupby按对的第二个元素进行分组,然后只考虑第一个值。

>>> from itertools import imap, groupby
>>> from operator import itemgetter
>>> formats = [(1, 'xxx'), (2, 'xxx'), (3, 'yyy'), (4, 'yyy')]
>>> for fmt, value in imap(lambda (x, y): (x, next(y)[0]), groupby(formats, itemgetter(1)):
...    print('%s: %s', fmt, value)
...
xxx: 1
yyy: 3

答案 5 :(得分:0)

如果您的清单符合规定,您可以确保相同的格式彼此相邻。这意味着您无需使用集合来跟踪过去的值。只需使用一个变量来记录最后处理的格式:

last = None
for (value, fmt) in formats:
    if fmt != last:
        last = fmt
        process(value, fmt)