【问题标题】:Remove duplicate sublists from a list从列表中删除重复的子列表
【发布时间】:2015-02-26 23:50:47
【问题描述】:

如果我有这样的清单:

mylist = [[1,2,3], ['a', 'c'], [3,4,5],[1,2], [3,4,5], ['a', 'c'], [3,4,5], [1,2]]

删除重复子列表的最佳方法是什么?

现在我用this

y, s = [ ], set( )
for t in mylist:
    w = tuple( sorted( t ) )
    if not w in s:
        y.append( t )
        s.add( w )

它有效,但我想知道是否有更好的方法?更像 python 的东西?

【问题讨论】:

标签: python list duplicates duplicate-removal


【解决方案1】:

将元素转换为元组*,然后将其整个转换为集合,然后将所有内容转换回列表:

m = [[1,2,3], ['a', 'c'], [3,4,5],[1,2], [3,4,5], ['a', 'c'], [3,4,5], [1,2]]

print [list(i) for i in set(map(tuple, m))]

*我们正在转换为元组,因为列表是不可散列的(因此我们不能对它们使用 set

【讨论】:

  • 谢谢。按我的意愿工作。
【解决方案2】:

您可以使用OrderedDict.fromkeys 将重复项从列表中过滤出来,同时仍保留顺序:

>>> from collections import OrderedDict
>>> mylist = [[1,2,3], ['a', 'c'], [3,4,5],[1,2], [3,4,5], ['a', 'c'], [3,4,5], [1,2]]
>>> map(list, OrderedDict.fromkeys(map(tuple, mylist)))
[[1, 2, 3], ['a', 'c'], [3, 4, 5], [1, 2]]
>>>

map(tuple, mylist) 是必需的,因为字典键必须是可散列的(列表不是,因为您可以从中添加/删除项目)。

【讨论】:

    【解决方案3】:

    好吧,既然sets 天生就是去重复数据,那么您的第一反应可能是set(mylist)。然而,这并不完全奏效:

    In [1]: mylist = [[1,2,3], ['a', 'c'], [3,4,5],[1,2], [3,4,5], ['a', 'c'], [3,4,5], [1,2]]
    
    In [2]: set(mylist)
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-2-b352bcae5975> in <module>()
    ----> 1 set(mylist)
    
    TypeError: unhashable type: 'list'
    

    这是因为sets 仅适用于iterables 的可散列元素(并且由于lists 是可变的,它们不可散列)。

    相反,您只需将子列表转换为子元组的代价即可:

    In [3]: set([tuple(x) for x in mylist])
    Out[3]: {(1, 2), (1, 2, 3), (3, 4, 5), ('a', 'c')}
    

    或者,如果您真的需要再次列出列表:

    In [4]: [list(x) for x in set([tuple(x) for x in mylist])]
    Out[4]: [[1, 2], [3, 4, 5], ['a', 'c'], [1, 2, 3]]
    

    【讨论】:

    • 有人愿意解释下否决票,因为它明显解决了 OP 的问题,如所述?
    • 我赞成你们取消反对票。说真的,那是什么回合?
    【解决方案4】:

    因为您的问题中有sorted(t),我假设您认为[1,2][2,1] 的重复项

    如果这是真的,我会为内部列表(可散列)使用 freezeset,并且不会关心子列表的顺序。

    比如:

    set(frozenset(sublist) for sublist in mylist)
    

    【讨论】:

    • 代码取自另一个问题,排序无关。
    【解决方案5】:

    您不需要排序,您复制的代码中的排序出于不同的原因进行排序:

    seen,out = set(), []
    
    for ele in mylist:
        tp = tuple(ele)
        if tp not in seen:
            out.append(ele)
        seen.add(tp)
    

    【讨论】:

      【解决方案6】:

      这将适用于您的情况:

      mylist2 = set(map(tuple, mylist))
      print(mylist2) # ('a', 'c'), (3, 4, 5), (1, 2), (1, 2, 3)}
      

      这行得通,因为它将您的子列表更改为元组,在您的情况下是可散列的。所以 set 可以带走它们并制作出独一无二的东西。

      如果您真的希望输出是列表列表,您可以这样做:

      print(list(map(list,mylist2))) # [['a', 'c'], [3, 4, 5], [1, 2], [1, 2, 3]]
      

      【讨论】:

      • 我赞成你们取消反对票。说真的,那是什么回合?
      • @Secret 谢谢。有些人似乎无缘无故地下载。
      • 可能是因为你得到的结果是一组元组而不是一个列表列表
      • OPs 代码也创建了一组元组。所以我想这对 OP 来说不是问题。
      • OP 将元组添加到集合而不是列表中
      【解决方案7】:

      如果顺序和结构(列表列表)无关紧要,您可以使用

      set(map(tuple, my_list))
      

      如果它们确实重要,您可以使用列表推导

      [e for i,e in enumerate(my_list) if e not in my_list[:i]]
      

      只保留每个元素的第一个副本,因此每个元素只保留一个。速度有点慢

      In [16]: timeit.timeit('[e for i,e in enumerate(my_list) if e not in my_list[:i]]', setup="my_list = [[1,2,3], ['a', 'c'], [3,4,5],[1,2], [3,4,5], ['a', 'c'], [3,4,5], [1,2]]")
      Out[16]: 1.9146944019994407
      
      In [17]: timeit.timeit('set(map(tuple, my_list))', setup="my_list = [[1,2,3], ['a', 'c'], [3,4,5],[1,2], [3,4,5], ['a', 'c'], [3,4,5], [1,2]]")
      Out[17]: 1.3857673469974543
      

      但如果您关心速度,您可能应该尝试一种循环方法。

      【讨论】:

        猜你喜欢
        • 2011-01-13
        • 2012-07-11
        • 2018-05-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多