【问题标题】:How to remove adjacent duplicate elements in a list using list comprehensions? [duplicate]如何使用列表推导删除列表中相邻的重复元素? [复制]
【发布时间】:2016-05-01 08:43:37
【问题描述】:

有没有办法在 python 中使用列表推导来过滤列表中的相邻重复项?

这是我的意思的一个例子:

>>> xs = [1,2,2,3]
>>> print added.reAdj(xs)
[1,2,3]

通过 SE 搜索发现 earlier inquiry 提出了一个类似但略有不同的问题:是否可以从列表中删除所有重复项,但没有明确要求涉及列表理解的解决方案。使用列表推导的动机特别遵循对their advantages over traditional for loops 的认可。用户建议使用 set() 函数或标准循环:

result = []
most_recent_elem = None
for e in xs:
    if e != most_recent_elem:
        result.append(e)
        most_recent_elem = e

set() 建议无法满足任务,即删除不相邻的重复项,而循环有效但冗长。

似乎需要一种安全引用列表推导中的下一个元素的方法,如下所示。

[x for x in xs if x != **x.next()**]

有什么想法吗?

【问题讨论】:

  • 这是否意味着[1,2,2,2,2,3,3,3,4,5,6,6] --> [1,2,3,4,5,6]
  • @IronFist,你理解正确。这就是想要的结果。
  • @Achampion。感谢您的链接,但据我所知,张贴者并没有专门要求涉及列表理解的答案。我最初的问题帖子包括一个循环解决方案。

标签: python list list-comprehension


【解决方案1】:

你可以使用itertools.groupby:

>>> import itertools
>>> [key for key, grp in itertools.groupby([1, 2, 2, 3])]
[1, 2, 3]

itertools.groupby 返回一个迭代器。通过迭代它,您将获得一个密钥,组对。 (如果没有指定 key 函数,key 将是一个项目,否则返回 key 函数的值)。 group 是一个迭代器,它将产生通过应用 key 函数分组的项目(如果未指定,相同的值将被分组)

>>> import itertools
>>> it = itertools.groupby([1, 2, 2, 3])
>>> it
<itertools.groupby object at 0x7feec0863048>
>>> for key, grp in it:
...     print(key)
...     print(grp)
... 
1
<itertools._grouper object at 0x7feec0828ac8>
2
<itertools._grouper object at 0x7feec0828b00>
3
<itertools._grouper object at 0x7feec0828ac8>
>>> it = itertools.groupby([1, 2, 2, 3])
>>> for key, grp in it:
...     print(list(grp))
... 
[1]
[2, 2]
[3]

以上解决方案,我只使用了key,因为问题不关心有多少项目是相邻的。

【讨论】:

  • 看到groupby 在没有排序输入的情况下使用真是太奇怪了,但我想这在这个特定的用例中实际上是合适的。
  • 同样的,一开始我很困惑
【解决方案2】:

您可以将list comprehensionenumerate 与@AChampion 建议的解决方案一起使用:

xs = [1,2,2,2,1,1]
In [115]: [n for i, n in enumerate(xs) if i==0 or n != xs[i-1]]
Out[115]: [1, 2, 1]

如果它是第一个,则该列表理解返回项目,如果它不等于上一个,则返回以下项目。由于对 if 语句的延迟评估,它会起作用。

【讨论】:

  • @AntonProtopopov。当你取得进展时请更新。不幸的是,Stefan Pochmann 提出的案例不起作用,因为您的解决方案在 IMO 中非常优雅。
  • @AntonProtopopov - ,请把 AChampion 的建议修复纳入您的答案。这比在 itertools 中对某些神秘函数的神秘调用要好得多,后者的文档又是神秘的。
  • @DavidHammen groupby 很简单,完全没问题,你不应该因为自己的不足而责备它。
  • @AntonProtopopov - 现在你得到一个减号。众所周知,xs[-1]是python中数组的最后一个元素。如果你不纠正这一点,你的答案就完全错误了。
  • 似乎错误很明显。我会接受这个答案,等待人们在接下来的几个小时内提出任何其他问题。
【解决方案3】:

使用 itertools 配方中的成对方法(使用 zip_longest)为您提供了一种检查下一个元素的简单方法:

import itertools as it

def pairwise(iterable):
    a, b = it.tee(iterable)
    next(b, None)
    return it.zip_longest(a, b, fillvalue=object())   # izip_longest for Py2

>>> xs = [1,2,2,3]
>>> [x for x, y in pairwise(xs) if x != y]
[1, 2, 3]
>>> xs = [1,2,2,2,2,3,3,3,4,5,6,6]
>>> [x for x, y in pairwise(xs) if x != y]
[1, 2, 3, 4, 5, 6]

【讨论】:

  • 轻微吹毛求疵:它会删除尾随 None 值。
  • 注意:但不是整数列表的问题。并且可以通过将fillvalue 添加到zip_longest 来避免,已修复!
【解决方案4】:

您可以使用不那么冗长的循环解决方案:

>>> result = xs[:1]
>>> for e in xs:
        if e != result[-1]:
            result.append(e)

或者:

>>> result = []
>>> for e in xs:
        if e not in result[-1:]:
            result.append(e)

【讨论】:

    【解决方案5】:

    这个怎么样:

    >>> l = [1,1,2,3,4,4,4,4,5,6,3,3,5,5,7,8,8,8,9,1,2,3,3,3,10,10]
    >>> 
    >>> o = []
    >>> p = None
    >>> for n in l:
            if n == p:
                continue
            o.append(n)
            p = n    
    
    >>> o
    [1, 2, 3, 4, 5, 6, 3, 5, 7, 8, 9, 1, 2, 3, 10]
    

    显然,上述解决方案比 OP 的解决方案更冗长,因此这里是使用来自 itertools 模块的 zip_longest 的替代方案:

    >>> l
    [1, 1, 2, 3, 4, 4, 4, 4, 5, 6, 3, 3, 5, 5, 7, 8, 8, 8, 9, 1, 2, 3, 3, 3, 10, 10]
    >>> from itertools import zip_longest
    >>> o = [p for p,n in zip_longest(l,l[1:]) if p != n] #By default fillvalue=None
    >>> o
    [1, 2, 3, 4, 5, 6, 3, 5, 7, 8, 9, 1, 2, 3, 10]
    

    【讨论】:

    • 它可以工作,但它与 OP 的逻辑完全相同,只是稍微冗长一些。
    • @AlexanderHuszagh .. 是的...我忘记了这一点 冗长
    • 更新 .. 使用zip_longest
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-02
    • 2020-11-02
    • 2020-05-07
    • 2020-01-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多