ChatGPT解决这个技术问题 Extra ChatGPT

如何在保留顺序的同时从列表中删除重复项?

如何在保留顺序的同时从列表中删除重复项?使用集合删除重复项会破坏原始顺序。有内置的或 Pythonic 的成语吗?

相关问题:In Python, what is the fastest algorithm for removing duplicates from a list so that all elements are unique while preserving order?

您可能需要考虑对这个答案 stackoverflow.com/a/17016257/1219006 进行 2020 年的编辑,这似乎是 Python 3.6(cpython)-7(all pythons)+ list(dict.fromkeys(items)) 现在的最佳解决方案

G
Georgy

这里有一些选择:http://www.peterbe.com/plog/uniqifiers-benchmark

最快的一个:

def f7(seq):
    seen = set()
    seen_add = seen.add
    return [x for x in seq if not (x in seen or seen_add(x))]

为什么将 seen.add 分配给 seen_add 而不仅仅是调用 seen.add? Python 是一种动态语言,每次迭代解析 seen.add 比解析局部变量的成本更高。 seen.add 可能在迭代之间发生了变化,并且运行时不够聪明,无法排除这种情况。为了安全起见,它必须每次都检查对象。

如果您计划在同一个数据集上大量使用此函数,那么使用有序集可能会更好:http://code.activestate.com/recipes/528878/

每个操作的插入、删除和成员检查 O(1)。

(补充一点:seen.add() 总是返回 None,所以上面的 or 只是作为尝试更新集合的一种方式,而不是作为逻辑测试的组成部分。 )


@JesseDhillon seen.add 可能在迭代之间发生了变化,并且运行时不够聪明,无法排除这种情况。为了安全起见,它必须每次都检查对象。 -- 如果使用 dis.dis(f) 查看字节码,您可以看到它在每次迭代中为 add 成员执行 LOAD_ATTRideone.com/tz1Tll
当我在列表列表上尝试此操作时,我得到: TypeError: unhashable type: 'list'
您的解决方案不是最快的。在 Python 3(未测试 2)中,这更快(300k 条目列表 - 0.045s(你的)vs 0.035s(这个):seen = set(); return [x for x in lines if x not in seen and not seen.add(x)]。我找不到你所做的 seen_add 行的任何速度效果。
@user136036 请链接到您的测试。您运行了多少次?seen_add 是一种改进,但时间可能会受到当时系统资源的影响。有兴趣看到完整的时间
对于编写 Python 代码的任何人来说,在牺牲可读性和普遍认可的 Python 约定之前,您真的应该三思而后行,只是为了在每个循环中多挤出几纳秒。使用和不使用 seen_add = seen.add 的测试只会使速度提高 1%。这几乎不重要。
1
11 revs, 6 users 61%

最佳解决方案因 Python 版本和环境限制而异:

Python 3.7+(以及大多数支持 3.6 的解释器,作为实现细节):

首先在 PyPy 2.5.0 中引入,并在 CPython 3.6 中作为实现细节采用,在 Python 3.7 中成为语言保证之前,普通 dict 是插入顺序的,甚至比 CPython 更有效(也是 C 实现的 CPython 3.5) collections.OrderedDict。因此,到目前为止,最快的解决方案也是最简单的:

>>> items = [1, 2, 0, 1, 3, 2]
>>> list(dict.fromkeys(items))  # Or [*dict.fromkeys(items)] if you prefer
[1, 2, 0, 3]

list(set(items)) 一样,这会将所有工作推到 C 层(在 CPython 上),但由于 dict 是插入排序的,因此 dict.fromkeys 不会丢失排序。它比 list(set(items)) 慢(通常需要 50-100% 的时间),但比任何其他订单保留解决方案快很多(大约需要 hacks involving use of sets in a listcomp 的一半时间)。

重要提示:来自 more_itertoolsunique_everseen 解决方案(见下文)在惰性和支持不可散列的输入项方面具有一些独特的优势;如果您需要这些功能,只有 的解决方案可以使用。

Python 3.5(以及所有旧版本,如果性能不重要)

作为 Raymond pointed out,在 CPython 3.5 中,OrderedDict 是用 C 实现的,丑陋的列表理解黑客比 OrderedDict.fromkeys 慢(除非您实际上需要最后的列表 - 即便如此,只有在输入非常短的情况下)。因此,在性能和可读性方面,CPython 3.5 的最佳解决方案是 OrderedDict 相当于 3.6+ 使用普通 dict

>>> from collections import OrderedDict
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(OrderedDict.fromkeys(items))
[1, 2, 0, 3]

在 CPython 3.4 及更早版本上,这将比其他一些解决方案慢,因此如果分析表明您需要更好的解决方案,请继续阅读。

Python 3.4 及更早版本,如果性能至关重要且第三方模块可接受

正如 @abarnert 所指出的,more_itertools 库 (pip install more_itertools) 包含一个 unique_everseen 函数,该函数是为解决此问题而构建的,没有任何不可读 (not seen.add) mutations 在列表推导中。这也是最快的解决方案:

>>> from more_itertools import unique_everseen
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(unique_everseen(items))
[1, 2, 0, 3]

只需一个简单的库导入,无需任何技巧。

该模块正在调整 itertools 配方 unique_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 filterfalse(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

但与 itertools 配方不同,它支持不可散列的项目(以性能为代价;如果 iterable 中的所有元素都是不可散列的,则算法变为 O(n²),如果它们都是可散列的,则算法变为 O(n) )。

重要提示:与此处的所有其他解决方案不同,unique_everseen 可以懒惰地使用;峰值内存使用量将相同(最终,底层 set 增长到相同大小),但如果您不list验证结果,只需对其进行迭代,您将能够处理独特的项目当它们被发现时,而不是等到整个输入被删除重复数据后再处理第一个唯一项目。

Python 3.4 及更早版本,如果性能至关重要且第三方模块不可用

你有两个选择:

将 unique_everseen 配方复制并粘贴到您的代码中,并按照上面的 more_itertools 示例使用它if x not in seen and not seen.add(x)] 以依赖于丑陋的 hack: not seen.add(x) 为代价,它依赖于 set.add 是一个始终返回 None 的就地方法这一事实所以不是 None 评估为 True。

请注意,上述所有解决方案都是 O(n)(保存对不可哈希项的可迭代项(即 O(n²))调用 unique_everseen,而其他解决方案将立即失败并返回 TypeError ),因此所有解决方案在不是最热门的代码路径时都具有足够的性能。使用哪一个取决于您可以依赖的语言规范/解释器/第三方模块的版本,性能是否至关重要(不要假设它是关键;通常不是),最重要的是可读性(因为如果维护此代码的人后来陷入了杀气,那么您聪明的微优化可能不值得)。


转换为某种自定义类型的 dict 只是为了获取密钥?只是另一个拐杖。
@Nakilon 我真的不明白它是如何拐杖的。它不会暴露任何可变状态,因此在这个意义上它非常干净。在内部,Python 集合是用 dict() (stackoverflow.com/questions/3949310/…) 实现的,所以基本上你只是在做解释器无论如何都会做的事情。
@EMS这不会保留订单。你也可以做seen = set(seq)
这个解决方案比提到的“hack”慢得多。对于我的 300k 条目列表,速度要慢 50 倍。
@CommuSoft 我同意,尽管实际上它几乎总是 O(n) 由于极不可能的最坏情况
S
ShadowRanger

在 CPython 3.6+(以及从 Python 3.7+ 开始的所有其他 Python 实现)中,字典 are ordered,因此从可迭代对象中删除重复项同时将其保留在原来的顺序是:

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

在 Python 3.5 及更低版本(包括 Python 2.7)中,使用 OrderedDict。我的时间安排表明,这现在是 Python 3.5 各种方法中最快和最短的方法(当它获得 C 实现时;在 3.5 之前,它仍然是最清晰的解决方案,尽管不是最快的)。

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

唯一的问题是可迭代的“元素”必须是可散列的 - 具有任意元素的可迭代对象(作为列表列表)的等价物会很好
字典上的插入顺序迭代提供了服务于更多用例而不是删除重复项的功能。例如,科学分析依赖于非确定性 dict 迭代不支持的可重现计算。可重复性是当前计算科学建模的主要目标,因此我们欢迎这一新功能。虽然我知道使用确定性 dict 构建是微不足道的,但高性能、确定性 set() 将帮助更多天真的用户开发可重现的代码。
使用 [*dict.fromkeys('abracadabra')](解包)而不是调用函数 list(...) 怎么样?在我的测试中,这更快,尽管只能检测到非常小的差异。所以我不确定这是否只是巧合。
@colidyre 是的,那会起作用。小的速度差异可能是由于操作员不必查找内置函数。还需要考虑一个明确的问题。
@RaymondHettinger:查找成本很小(使用 3.8 的 LOAD_GLOBAL 变得更小);主要优点是避免了构造函数代码路径(需要为 args 构造 tuple 并将 NULL 指针作为 kwargs dict 传递,然后分别调用大部分为空的 __new____init__,后者则必须通过广义参数解析代码,全部传递 0-1 位置参数)。不过,从 3.9 开始,list() 通过 vectorcall 协议绕过了大部分,将我机器上的增量收益从 60-70 ns (3.8.5) 减少到 20-30 ns (3.10.0)。
A
Alexander

不要踢死马(这个问题很老,已经有很多好的答案),但这里有一个使用 pandas 的解决方案,它在许多情况下都非常快,而且使用起来非常简单。

import pandas as pd

my_list = [0, 1, 2, 3, 4, 1, 2, 3, 5]

>>> pd.Series(my_list).drop_duplicates().tolist()
# Output:
# [0, 1, 2, 3, 4, 5]

有用,但不保留顺序。 more_itertools.unique_everseen 可以。
t
timgeb

Python 3.7 及更高版本中,字典guaranteed要记住它们的键插入顺序。 this问题的答案总结了当前的事态。

OrderedDict 解决方案因此变得过时并且没有任何导入语句,我们可以简单地发出:

>>> lst = [1, 2, 1, 3, 3, 2, 4]
>>> list(dict.fromkeys(lst))
[1, 2, 3, 4]

H
Honest Abe
sequence = ['1', '2', '3', '3', '6', '4', '5', '6']
unique = []
[unique.append(item) for item in sequence if item not in unique]

独特的 → ['1', '2', '3', '6', '4', '5']


值得注意的是,它在 n^2 中运行
伊克。 2 次打击:使用列表进行成员资格测试(慢,O(N) 每个测试)并使用列表推导来解决副作用(在此过程中构建另一个引用列表!)
我同意@MartijnPieters 的观点,绝对没有 理由让列表理解产生副作用。只需使用 for 循环
n
ninjagecko
from itertools import groupby
[ key for key,_ in groupby(sortedList)]

列表甚至不必排序,充分条件是相等的值被组合在一起。

编辑:我假设“保留顺序”意味着列表实际上是有序的。如果不是这种情况,那么 MizardX 的解决方案就是正确的。

社区编辑:然而,这是“将重复的连续元素压缩为单个元素”的最优雅的方式。


但这并不能保持秩序!
Hrm,这是有问题的,因为我不能保证相等的值被分组在一起而不在列表上循环一次,到那时我可以修剪重复项。
我认为“保留顺序”意味着列表实际上是有序的。
也许输入列表的规范有点不清楚。这些值甚至不需要组合在一起:[2, 1, 3, 1]。那么要保留哪些值,要删除哪些值呢?
@igorkf 忽略对的第二个元素。
s
shamrock

我想如果你想维持秩序,

你可以试试这个:

list1 = ['b','c','d','b','c','a','a']    
list2 = list(set(list1))    
list2.sort(key=list1.index)    
print list2

或者同样你可以这样做:

list1 = ['b','c','d','b','c','a','a']  
list2 = sorted(set(list1),key=list1.index)  
print list2 

你也可以这样做:

list1 = ['b','c','d','b','c','a','a']    
list2 = []    
for i in list1:    
    if not i in list2:  
        list2.append(i)`    
print list2

也可以这样写:

list1 = ['b','c','d','b','c','a','a']    
list2 = []    
[list2.append(i) for i in list1 if not i in list2]    
print list2 

您的前两个答案假设可以使用排序功能重建列表的顺序,但事实可能并非如此。
大多数答案都集中在性能上。对于不够大而不必担心性能的列表, sorted(set(list1),key=list1.index) 是我见过的最好的东西。没有额外的导入,没有额外的函数,没有额外的变量,而且相当简单易读。
M
MSeifert

只是为了从外部模块添加此类功能的另一个(非常高效的)实现1iteration_utilities.unique_everseen

>>> from iteration_utilities import unique_everseen
>>> lst = [1,1,1,2,3,2,2,2,1,3,4]

>>> list(unique_everseen(lst))
[1, 2, 3, 4]

计时

我做了一些计时(Python 3.6),这些表明它比我测试的所有其他替代方案更快,包括 OrderedDict.fromkeysf7more_itertools.unique_everseen

%matplotlib notebook

from iteration_utilities import unique_everseen
from collections import OrderedDict
from more_itertools import unique_everseen as mi_unique_everseen

def f7(seq):
    seen = set()
    seen_add = seen.add
    return [x for x in seq if not (x in seen or seen_add(x))]

def iteration_utilities_unique_everseen(seq):
    return list(unique_everseen(seq))

def more_itertools_unique_everseen(seq):
    return list(mi_unique_everseen(seq))

def odict(seq):
    return list(OrderedDict.fromkeys(seq))

from simple_benchmark import benchmark

b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
              {2**i: list(range(2**i)) for i in range(1, 20)},
              'list size (no duplicates)')
b.plot()

https://i.stack.imgur.com/XLrov.png

并且只是为了确保我还进行了更多重复的测试,以检查它是否有所作为:

import random

b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
              {2**i: [random.randint(0, 2**(i-1)) for _ in range(2**i)] for i in range(1, 20)},
              'list size (lots of duplicates)')
b.plot()

https://i.stack.imgur.com/YCx2c.png

一个只包含一个值:

b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
              {2**i: [1]*(2**i) for i in range(1, 20)},
              'list size (only duplicates)')
b.plot()

https://i.stack.imgur.com/SPCcT.png

在所有这些情况下,iteration_utilities.unique_everseen 函数是最快的(在我的计算机上)。

iteration_utilities.unique_everseen 函数还可以处理输入中不可散列的值(但是,当这些值是可散列的时,其性能是 O(n*n) 而不是 O(n))。

>>> lst = [{1}, {1}, {2}, {1}, {3}]

>>> list(unique_everseen(lst))
[{1}, {2}, {3}]

1 免责声明:我是该软件包的作者。


我不明白这一行的必要性:seen_add = seen.add - 基准测试需要这样做吗?
@Alex 这是 this answer 中给出的方法。在那里问它会更有意义。我只是使用该答案中的方法来比较时间。
您可以将 dict.fromkeys() 方法添加到您的图表吗?
我不太确定我是否也有同样的事情要尽快安排时间。您认为它比 ordereddict.fromkeys 快得多吗?
“这个 iteration_utilities.unique_everseen 函数也可以处理输入中不可散列的值”——是的,这非常重要。如果您有一个 dicts of dicts 等的 dicts 列表,这是完成这项工作的唯一方法,即使是小规模的也是如此。
a
abarnert

对于另一个非常古老的问题的另一个很晚的答案:

itertools recipes 具有使用 seen 集合技术执行此操作的函数,但是:

处理标准键功能。

不使用不合时宜的技巧。

通过预先绑定 seen.add 来优化循环,而不是查找 N 次。 (f7 也这样做,但有些版本没有。)

使用 ifilterfalse 优化循环,因此您只需遍历 Python 中的唯一元素,而不是所有元素。 (当然,您仍然在 ifilterfalse 中迭代所有这些,但这是在 C 中,而且速度要快得多。)

它实际上比 f7 快吗?这取决于您的数据,因此您必须对其进行测试并查看。如果您最后想要一个列表,f7 使用 listcomp,而这里没有办法做到这一点。 (您可以直接 append 而不是 yield ,或者您可以将生成器输入 list 函数,但两者都不能像 listcomp 中的 LIST_APPEND 一样快。)无论如何,通常,挤出几微秒并不像拥有一个易于理解、可重用、已经编写好的函数那样重要,当你想要装饰时不需要 DSU。

与所有食谱一样,它也可在 more-iterools 中找到。

如果您只想要 no-key 情况,您可以将其简化为:

def unique(iterable):
    seen = set()
    seen_add = seen.add
    for element in itertools.ifilterfalse(seen.__contains__, iterable):
        seen_add(element)
        yield element

我完全忽略了 more-itertools 这显然是最好的答案。一个简单的from more_itertools import unique_everseen list(unique_everseen(items))比我的方法快得多,比公认的答案好得多,我认为图书馆下载是值得的。我要去社区维基我的答案并添加它。
z
zmk

对于基于 MizardX 的无哈希类型(例如列表列表):

def f7_noHash(seq)
    seen = set()
    return [ x for x in seq if str( x ) not in seen and not seen.add( str( x ) )]

S
Sergey Bershadsky

减少变体速度快 5 倍,但更复杂

>>> l = [5, 6, 6, 1, 1, 2, 2, 3, 4]
>>> reduce(lambda r, v: v in r[1] and r or (r[0].append(v) or r[1].add(v)) or r, l, ([], set()))[0]
[5, 6, 1, 2, 3, 4]

解释:

default = (list(), set())
# use list to keep order
# use set to make lookup faster

def reducer(result, item):
    if item not in result[1]:
        result[0].append(item)
        result[1].add(item)
    return result

>>> reduce(reducer, l, default)[0]
[5, 6, 1, 2, 3, 4]

A
Ahmed4end

这是一个简单的方法:

list1 = ["hello", " ", "w", "o", "r", "l", "d"]
sorted(set(list1 ), key=list1.index)

这给出了输出:

["hello", " ", "w", "o", "r", "l", "d"]

t
timgeb

熊猫用户应查看 pandas.unique

>>> import pandas as pd
>>> lst = [1, 2, 1, 3, 3, 2, 4]
>>> pd.unique(lst)
array([1, 2, 3, 4])

该函数返回一个 NumPy 数组。如果需要,您可以使用 tolist 方法将其转换为列表。


好东西。我永远不会想象为此使用熊猫,但它确实有效
list(pd.unique(a)) 会将其转换为 OP 想要的普通列表。赞成熊猫解决方案。从来没有想过这样做。
pd.unique(lst).tolist() 是更好的成语。抄送:@JoeFerndz
e
ely

借用在为列表定义 Haskell 的 nub 函数时使用的递归思想,这将是一种递归方法:

def unique(lst):
    return [] if lst==[] else [lst[0]] + unique(filter(lambda x: x!= lst[0], lst[1:]))

例如:

In [118]: unique([1,5,1,1,4,3,4])
Out[118]: [1, 5, 4, 3]

我尝试使用它来增加数据大小并看到亚线性时间复杂度(不是确定的,但建议这对于正常数据应该没问题)。

In [122]: %timeit unique(np.random.randint(5, size=(1)))
10000 loops, best of 3: 25.3 us per loop

In [123]: %timeit unique(np.random.randint(5, size=(10)))
10000 loops, best of 3: 42.9 us per loop

In [124]: %timeit unique(np.random.randint(5, size=(100)))
10000 loops, best of 3: 132 us per loop

In [125]: %timeit unique(np.random.randint(5, size=(1000)))
1000 loops, best of 3: 1.05 ms per loop

In [126]: %timeit unique(np.random.randint(5, size=(10000)))
100 loops, best of 3: 11 ms per loop

我还认为有趣的是,这可以很容易地通过其他操作推广到唯一性。像这样:

import operator
def unique(lst, cmp_op=operator.ne):
    return [] if lst==[] else [lst[0]] + unique(filter(lambda x: cmp_op(x, lst[0]), lst[1:]), cmp_op)

例如,您可以传入一个函数,该函数使用舍入到相同整数的概念,就好像它是“相等”一样,以实现唯一性目的,如下所示:

def test_round(x,y):
    return round(x) != round(y)

那么 unique(some_list, test_round) 将提供列表的唯一元素,其中唯一性不再意味着传统的相等性(这是通过使用任何类型的基于集合或基于字典键的方法来解决这个问题)而是意味着采取对于元素可能舍入到的每个可能的整数 K,仅舍入到 K 的第一个元素,例如:

In [6]: unique([1.2, 5, 1.9, 1.1, 4.2, 3, 4.8], test_round)
Out[6]: [1.2, 5, 1.9, 4.2, 3]

请注意,当唯一元素的数量相对于元素总数非常大时,性能会变差,因为每次连续递归调用对 filter 的使用几乎不会从前一次调用中受益。但是如果唯一元素的数量相对于数组大小来说很小,那么这应该会表现得很好。
C
Community

您可以引用一个列表推导,因为它是由符号“_[1]”构建的。例如,以下函数通过引用其列表推导来唯一化一个元素列表而不改变它们的顺序。

def unique(my_list): 
    return [x for x in my_list if x not in locals()['_[1]']]

演示:

l1 = [1, 2, 3, 4, 1, 2, 3, 4, 5]
l2 = [x for x in l1 if x not in locals()['_[1]']]
print l2

输出:

[1, 2, 3, 4, 5]

另请注意,它将使其成为 O(n^2) 操作,其中创建 set/dict(具有恒定的查找时间)并仅添加以前未见过的元素将是线性的。
这只是我相信的 Python 2.6。是的,它是 O(N^2)
@jamylak 的意思是这仅适用于 Python 2.7 及更早版本,而不是更高版本。
@GlennSlayden 不,我的意思只是 Python 2.6。 Python 2.6 及更早版本(不确定究竟早了多少)。 Python 2.6 在当时更流行,所以我说 Python 2.6 只是与 Python 2.7 相比
@jamylak 好的,但我的意思是,不是任何 Python 3.x,我在 2015 年 6 月 7 日的评论中并不清楚。
G
Glenn Slayden

1. 这些解决方案很好......为了在保留顺序的同时删除重复项,本页其他地方提出的优秀解决方案:

seen = set()
[x for x in seq if not (x in seen or seen.add(x))]

和变体,例如:

seen = set()
[x for x in seq if x not in seen and not seen.add(x)]

确实很受欢迎,因为它们简单、简约,并且部署了正确的散列以获得最佳效率。关于这些的主要抱怨似乎是使用方法 seen.add(x) “返回”的不变 None 作为逻辑表达式中的常量(因此是多余/不必要的)值 - 只是为了它的副作用 - 是 hacky 和/或令人困惑。

2。 …但他们每次迭代都浪费了一次哈希查找。
令人惊讶的是,考虑到关于这个主题的大量讨论和辩论,实际上对似乎被忽视的代码进行了重大改进。如图所示,每次“测试和设置”迭代都需要 两次 哈希查找:第一次测试成员资格 x not in seen,然后再次实际添加值 seen.add(x)。由于第一个操作保证了第二个操作总是成功的,所以这里的重复工作是浪费的。而且由于这里的整体技术非常有效,多余的哈希查找可能最终成为剩下的少量工作中最昂贵的部分。

3。相反,让 set 完成它的工作!
请注意,上面的示例仅调用 set.add 并预先知道这样做会导致集合成员资格的增加。 set 本身永远没有机会拒绝重复;我们的代码片段基本上已经为自己篡夺了这个角色。使用明确的两步测试和设置代码正在剥夺 set 自身排除这些重复项的核心能力。

4. 改进的代码:以下版本将每次迭代的哈希查找次数减少了一半——从两次减少到只有一次。这大大提高了已经很快速的方法的性能。

seen = set()
[x for x in seq if len(seen) < len(seen.add(x) or seen)]

至于令人不快的黑客攻击,现在比以前发生了一些变异,它似乎确实可以继续看到另一天。


c
code22

如果您需要一个班轮,那么这可能会有所帮助:

reduce(lambda x, y: x + y if y[0] not in x else x, map(lambda x: [x],lst))

...应该可以,但如果我错了,请纠正我


这是一个conditional expression,所以很好
在 Python 3 中,您必须from functools import reduce
j
jamylak

MizardX 的答案提供了多种方法的良好集合。

这是我在大声思考时想到的:

mylist = [x for i,x in enumerate(mylist) if x not in mylist[i+1:]]

您的解决方案很好,但它需要每个元素的最后一次出现。要第一次出现,请使用: [x for i,x in enumerate(mylist) if x not in mylist[:i]]
由于在列表中搜索是一项 O(n) 操作,并且您对每个项目都执行此操作,因此您的解决方案的最终复杂性将是 O(n^2)。对于这样一个微不足道的问题,这是不可接受的。
佚名

你可以做一种丑陋的列表理解黑客。

[l[i] for i in range(len(l)) if l.index(l[i]) == i]

首选 i,e in enumerate(l) 而不是 l[i] for i in range(len(l))
G
Grijesh Chauhan

使用 _sorted_numpy 数组的相对有效的方法:

b = np.array([1,3,3, 8, 12, 12,12])    
numpy.hstack([b[0], [x[0] for x in zip(b[1:], b[:-1]) if x[0]!=x[1]]])

输出:

array([ 1,  3,  8, 12])

k
kylieCatt
l = [1,2,2,3,3,...]
n = []
n.extend(ele for ele in l if ele not in set(n))

使用 O(1) 查找集合来确定是否在新列表中包含元素的生成器表达式。


巧妙地将 extend 与依赖于被扩展事物的生成器表达式一起使用(所以 +1),但是 set(n) 在每个阶段都被重新计算(这是线性的),这使整体方法变成了二次方。事实上,这几乎肯定比简单地使用 ele in n 更糟糕。为单个成员资格测试制作一个集合不值得为创建集合而付出代价。不过——这是一种有趣的方法。
I
Ilya Prokin

一个简单的递归解决方案:

def uniquefy_list(a):
    return uniquefy_list(a[1:]) if a[0] in a[1:] else [a[0]]+uniquefy_list(a[1:]) if len(a)>1 else [a[0]]

t
this.srivastava

消除序列中的重复值,但保留剩余项目的顺序。使用通用生成器功能。

# for hashable sequence
def remove_duplicates(items):
    seen = set()
    for item in items:
        if item not in seen:
            yield item
            seen.add(item)

a = [1, 5, 2, 1, 9, 1, 5, 10]
list(remove_duplicates(a))
# [1, 5, 2, 9, 10]



# for unhashable sequence
def remove_duplicates(items, key=None):
    seen = set()
    for item in items:
        val = item if key is None else key(item)
        if val not in seen:
            yield item
            seen.add(val)

a = [ {'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 1, 'y': 2}, {'x': 2, 'y': 4}]
list(remove_duplicates(a, key=lambda d: (d['x'],d['y'])))
# [{'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 2, 'y': 4}]

F
Franco
x = [1, 2, 1, 3, 1, 4]

# brute force method
arr = []
for i in x:
  if not i in arr:
    arr.insert(x[i],i)

# recursive method
tmp = []
def remove_duplicates(j=0):
    if j < len(x):
      if not x[j] in tmp:
        tmp.append(x[j])
      i = j+1  
      remove_duplicates(i)

      

remove_duplicates()

J
Jože Ws

一个班轮列表理解:

values_non_duplicated = [value for index, value in enumerate(values) if value not in values[ : index]]

L
Lei

如果您经常使用 pandas,并且美观优于性能,那么请考虑内置函数 pandas.Series.drop_duplicates

    import pandas as pd
    import numpy as np

    uniquifier = lambda alist: pd.Series(alist).drop_duplicates().tolist()

    # from the chosen answer 
    def f7(seq):
        seen = set()
        seen_add = seen.add
        return [ x for x in seq if not (x in seen or seen_add(x))]

    alist = np.random.randint(low=0, high=1000, size=10000).tolist()

    print uniquifier(alist) == f7(alist)  # True

定时:

    In [104]: %timeit f7(alist)
    1000 loops, best of 3: 1.3 ms per loop
    In [110]: %timeit uniquifier(alist)
    100 loops, best of 3: 4.39 ms per loop

S
Soham Joshi

这将保持秩序并在 O(n) 时间内运行。基本上这个想法是在找到重复的地方创建一个洞并将其沉入底部。使用读写指针。每当找到重复项时,只有读取指针前进,而写入指针停留在重复项上以覆盖它。

def deduplicate(l):
    count = {}
    (read,write) = (0,0)
    while read < len(l):
        if l[read] in count:
            read += 1
            continue
        count[l[read]] = True
        l[write] = l[read]
        read += 1
        write += 1
    return l[0:write]

R
Rob Murray

不使用导入模块或集合的解决方案:

text = "ask not what your country can do for you ask what you can do for your country"
sentence = text.split(" ")
noduplicates = [(sentence[i]) for i in range (0,len(sentence)) if sentence[i] not in sentence[:i]]
print(noduplicates)

给出输出:

['ask', 'not', 'what', 'your', 'country', 'can', 'do', 'for', 'you']

这是 O(N**2) 复杂度 + 每次列表切片。
g
gboffi

就地方法

这种方法是二次的,因为我们对列表中的每个元素进行了线性查找(为此,我们必须加上重新排列列表的成本,因为 del s)。

也就是说,如果我们从列表的末尾开始向原点前进,删除其左侧子列表中存在的每个术语,则可以就地操作

代码中的这个想法很简单

for i in range(len(l)-1,0,-1): 
    if l[i] in l[:i]: del l[i] 

一个简单的实现测试

In [91]: from random import randint, seed                                                                                            
In [92]: seed('20080808') ; l = [randint(1,6) for _ in range(12)] # Beijing Olympics                                                                 
In [93]: for i in range(len(l)-1,0,-1): 
    ...:     print(l) 
    ...:     print(i, l[i], l[:i], end='') 
    ...:     if l[i] in l[:i]: 
    ...:          print( ': remove', l[i]) 
    ...:          del l[i] 
    ...:     else: 
    ...:          print() 
    ...: print(l)
[6, 5, 1, 4, 6, 1, 6, 2, 2, 4, 5, 2]
11 2 [6, 5, 1, 4, 6, 1, 6, 2, 2, 4, 5]: remove 2
[6, 5, 1, 4, 6, 1, 6, 2, 2, 4, 5]
10 5 [6, 5, 1, 4, 6, 1, 6, 2, 2, 4]: remove 5
[6, 5, 1, 4, 6, 1, 6, 2, 2, 4]
9 4 [6, 5, 1, 4, 6, 1, 6, 2, 2]: remove 4
[6, 5, 1, 4, 6, 1, 6, 2, 2]
8 2 [6, 5, 1, 4, 6, 1, 6, 2]: remove 2
[6, 5, 1, 4, 6, 1, 6, 2]
7 2 [6, 5, 1, 4, 6, 1, 6]
[6, 5, 1, 4, 6, 1, 6, 2]
6 6 [6, 5, 1, 4, 6, 1]: remove 6
[6, 5, 1, 4, 6, 1, 2]
5 1 [6, 5, 1, 4, 6]: remove 1
[6, 5, 1, 4, 6, 2]
4 6 [6, 5, 1, 4]: remove 6
[6, 5, 1, 4, 2]
3 4 [6, 5, 1]
[6, 5, 1, 4, 2]
2 1 [6, 5]
[6, 5, 1, 4, 2]
1 5 [6]
[6, 5, 1, 4, 2]

In [94]:                                                                                                                             

在发布之前,我已经搜索了“地点”的答案正文,但无济于事。如果其他人以类似的方式解决了问题,请提醒我,我会尽快删除我的答案。
如果您想要就地操作,您可以只使用 l[:] = <one of the the faster methods>,不是吗?
@timgeb 是与否……当我执行 a=[1]; b=a; a[:]=[2] 时,b==[2] 值为 True,我们可以说我们正在就地执行它,但是您建议使用新空间来创建新列表,替换旧数据与新数据,并将旧数据标记为垃圾收集,因为不再被任何东西引用,所以说它在原地运行有点延伸了我所展示的概念,这是可能的......是吗低效?是的,但我已经提前说过了。