【问题标题】:What is the cleanest way to do a sort plus uniq on a Python list?在 Python 列表上进行排序加 uniq 的最简洁方法是什么?
【发布时间】:2011-02-25 06:38:55
【问题描述】:

考虑一个包含['foo', 'foo', 'bar'] 的Python 列表my_list

uniquify 和对列表进行排序的最 Pythonic 方式是什么?
(想想cat my_list | sort | uniq

这是我目前的做法,虽然它有效,但我确信还有更好的方法。

my_list = []
...
my_list.append("foo")
my_list.append("foo")
my_list.append("bar")
...
my_list = set(my_list)
my_list = list(my_list)
my_list.sort()

【问题讨论】:

标签: python unique


【解决方案1】:
# Python ≥ 2.4
# because of (generator expression) and itertools.groupby, sorted

import itertools

def sort_uniq(sequence):
    return (x[0] for x in itertools.groupby(sorted(sequence)))

更快:

import itertools, operator
import sys

if sys.hexversion < 0x03000000:
    mapper= itertools.imap # 2.4 ≤ Python < 3
else:
    mapper= map # Python ≥ 3

def sort_uniq(sequence):
    return mapper(
        operator.itemgetter(0),
        itertools.groupby(sorted(sequence)))

两个版本都返回一个生成器,因此您可能希望将结果提供给列表类型:

sequence= list(sort_uniq(sequence))

请注意,这也适用于不可散列的项目:

>>> list(sort_uniq([[0],[1],[0]]))
[[0], [1]]

【讨论】:

  • 如果你使用 python3: Py3 map 和 Py2 itertools.imap 做同样的事情。 (在 Py3 中 iter(map(...)) 是多余的。)
  • 假设您有大量数据,这比公认的答案要好得多。 +1
  • @TheDemz 需要考虑到 Python 3 现在比那时更普遍的答案;谢谢
  • 注意x[0](或operator.itemgetter(0))如果你使用key参数来决定元素之间的一些替代相等性,那么groupby将不起作用(大致是相当于使用-f-s 作为uniq 的参数)。在这种情况下,键与输入数据元素不同。我认为在这种情况下,next(iter(x[1])) 之类的东西可以解决每个“根据关键功能相同”组的第一个元素。
【解决方案2】:

其他人提到了 sorted(set(my_list)),它适用于字符串、数字和元组等可散列值,但不适用于列表等不可散列类型。

获取任何可排序类型的排序列表,没有重复:

from itertools import izip, islice
def unique_sorted(values):
    "Return a sorted list of the given values, without duplicates."
    values = sorted(values)
    if not values:
        return []
    consecutive_pairs = izip(values, islice(values, 1, len(values)))
    result = [a for (a, b) in consecutive_pairs if a != b]
    result.append(values[-1])
    return result

这可以使用itertools documentation 中的“pairwise”或“unique_justseen”配方进一步简化。

【讨论】:

    【解决方案3】:

    Ignacio 提供了直接的解决方案—sorted(set(foo))

    如果您有唯一的数据,那么您很有可能不想只做sorted(set(...)),而是一直存储一个集合并偶尔提取值的排序版本。 (那时,它开始听起来像是人们经常使用数据库来做的事情。)

    如果您有一个排序列表,并且您想以对数检查成员资格并在最坏的线性时间添加一个项目,您可以使用bisect module

    如果你想一直保持这个条件,想要简化一些事情或者让一些操作执行得更好,你可以考虑blist.sortedset

    【讨论】:

    【解决方案4】:

    不能说这样做很干净,但只是为了好玩:

    my_list = [x for x in sorted(my_list) if not x in locals()["_[1]"]]
    

    【讨论】:

    • 当然,正如我所说,这只是为了好玩。
    【解决方案5】:
    my_list = sorted(set(my_list))
    

    【讨论】:

    • 请注意,这仅适用于可散列类型,因此例如这不适用于列表。
    • 值得一提的是,当sort(通常?)知道要持久化到磁盘时,它会在内存中执行所有操作。如果您将此应用于大量数据,它应该会在MemoryError 上失败。不过答案很好:)
    • @ReutSharabani:不,不同的是sort() 方法就地运行,因此不需要额外分配。
    • 不确定你的意思...如果不是全部,大多数sorts 会在需要时写入光盘。
    • 排序后跟一个就地唯一性是一种比将列表转换为集合然后对其进行排序更有效的操作。即使使用最小堆也会更好。
    猜你喜欢
    • 2013-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-29
    相关资源
    最近更新 更多