从列表中删除重复项,但要考虑元素的类型并保留顺序

时间:2020-01-29 18:29:27

标签: python list types

任务:

开发一个clean_list (list_to_clean)函数, 它带有一个参数-任意长度的任何值(字符串,整数和浮点数)的列表, 并返回一个具有相同值但没有重复项的列表。这意味着,如果原始列表在多个实例中都存在一个值,则该值的第一个“实例”将保留在原位置,而第二个,第三个等等将被删除。

示例:

函数调用:clean_list ([32, 32.1, 32.0, -32, 32, '32']) 返回:[32, 32.1, 32.0, -32, '32']

我的代码:

def clean_list(list_to_clean):
   no_dubl_lst = [value for _, value in set((type(x), x) for x in list_to_clean)]
   return no_dubl_lst

print(clean_list([32, 32.1, 32.0, -32, 32, '32']))

结果:

[32.1, 32, -32, 32.0, '32']

但是我如何恢复原始订单?

6 个答案:

答案 0 :(得分:4)

这里有两个问题,因此出于回答的目的,我将两者都列出。

指定类型(您已经弄清楚了)

Removing duplicates in lists建议将中间体set构造为最快的方法。如果一个元素等于当前元素,则认为它存在于集合中。

在您的情况下,您不仅需要值,还需要类型相等。 那么,为什么不构造一组中间元组(value, type)

unique_list = [v for v,t in {(v,type(v)) for v in orig_list}]

保存顺序

使用Does Python have an ordered set?中的“有序集”容器。例如:

  • 从3.7(和CPython 3.6,其中是实现细节)开始,常规dict保留插入顺序:

    unique_list = [v for v,t in dict.fromkeys((v,type(v)) for v in orig_list)]
    
  • 对于所有版本(在3.6+中也存在,因为它还有其他方法),请使用collections.OrderedDict

    import collections
    unique_list = [v for v,t in collections.OrderedDict.fromkeys((v,type(v)) for v in orig_list)]
    

作为参考,与撰写本文时的其他答案相比,timeit在我的计算机(3.7.4 win64)上得到的结果:

In [24]: l=[random.choice((int,float,lambda v:str(int(v))))(random.random()*1000) for _ in range(100000)]

In [26]: timeit dict_fromkeys(l)        #mine
38.6 ms ± 179 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [34]: timeit ordereddict_fromkeys(l)  #mine with OrderedDict
53.3 ms ± 233 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [25]: timeit build_with_filter(l)    #Ch3steR's O(n)
48.7 ms ± 214 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [28]: timeit dict_with_none(l)       #Patrick Artner's
46.8 ms ± 377 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [30]: timeit listcompr_side_effect(l)  #CDJB's
55.5 ms ± 801 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

答案 1 :(得分:3)

放置集,检查集方法很好。您也可以将任何dict()用于python 3.7+(键保持输入顺序)或OrderedDict用于python 3.7以下版本:

def clean_list(list_to_clean):
    ord_dic = dict

    import sys
    major,minor,_,_,_ = sys.version_info

    if major < 3 or major == 3 and minor < 7: 
        # dict not yet input ordered
        from collections import OrderedDict
        no_dubl_lst = OrderedDict(((type(a),a),None) for a in list_to_clean)
    else:
        # dict is input ordered by default
        no_dubl_lst = dict(((type(a),a),None) for a in list_to_clean)


    return list(b for _,b in no_dubl_lst.keys()) # only produce the actual data

print(clean_list([32, 32.1, 32.0, -32, 32, '32']))

产生:

[32, 32.1, 32.0, -32, '32']

基本上,字典键的行为类似于一组键(仅会使用第一个,而后面的键会覆盖第一个的None值)-但它们是有序的。

如果您使用的是3.7+简易版

def clean_list(list_to_clean):
        # dict is input ordered by default
        no_dubl_lst = dict(((type(a),a),None) for a in list_to_clean)

类似于ivan_pozdeev的答案-在我创建答案时,他删除/编辑/取消删除了具有更多内容的第一个答案。

您将获得我的自动版本检测功能,因此请不要将其删除。

答案 2 :(得分:2)

您尝试一下。

像您要求的那样第一个实例保留在原处,其余的都会被删除
(请注意,这个是O(n^2)

_list=[32, 32.1, 32.0, -32, 32, '32']
_clist=[]
for i in _list:
    if (i,type(i),) not in _clist:
        _clist.append((i,type(i),))
cleaned_list=list(zip(*_clist))[0]
print(cleaned_list)
#(32, 32.1, 32.0, -32, '32')

一种O(n)方法,具有额外的O(n)空间。

_list=[32, 32.1, 32.0, -32, 32, '32']
unique=set()
cleaned=[]
for i in _list:
    if (i,type(i),) not in unique:
        unique.add((i,type(i),))
        cleaned.append(i)

注意:

请检查timeit的答案,以对发布的几个答案进行stock分析。

答案 3 :(得分:0)

为此,我的版本是O(n)

编辑后的答案:现在,它比普通设置更尊重类型

def clean_list(list_to_cealn):
    unique_items = set()
    result = []
    for x in list_to_cealn:
        item = (type(x), x)
        if item not in unique_items:
            result.append(x)
            unique_items.add(item)
    return result

答案 4 :(得分:0)

适应方法here

def clean_list(list_to_clean):
   seen = set()
   seen_add = seen.add
   no_dubl_lst = [value for _, value in [(type(x), x) for x in list_to_clean if not ( (type(x), x) in seen or seen_add((type(x), x)))]]
   return no_dubl_lst

print(clean_list([32, 32.1, 32.0, -32, 32, '32']))

输出:

[32, 32.1, 32.0, -32, '32']

答案 5 :(得分:0)

保留dict而不是集合,以将(x, type(x))映射到x

我们第一次看到x类型为y的情况,并不是(x,y)是字典中的键。

但是,有一种实用的方法可以同时更新dict 以获得所需的值:使用setdefault方法。

在下面,我们首先创建元组(并使用:=来记住它)并检查它是否在seen中。这将不是我们第一次遇到特定类型的x。由于t not in seenTrue,因此我们将评估对setdefault的调用。然后,该方法调用将返回x的值,但也有副作用,请执行seen[t] = x。现在,当我们发现重复项时,t not in seen将返回False,从而完全避免了seen.setdefault的调用,从而跳过了x的重复值。由于我们仅迭代原始输入,因此原始订单保留在输出中。

seen = {}
no_dubl_lst = [seen.setdefault(t, x)
               for x in list_to_clean
               if (t:=(x, type(x))) not in seen]
相关问题