如何从列表中删除不区分大小写的重复项,同时保持原始列表顺序?



>我有一个字符串列表,例如:

myList = ["paper", "Plastic", "aluminum", "PAPer", "tin", "glass", "tin", "PAPER", "Polypropylene Plastic"]

我想要这个结果(这是唯一可接受的结果):

myList = ["paper", "Plastic", "aluminum", "tin", "glass", "Polypropylene Plastic"]

请注意,如果一个项目("Polypropylene Plastic")恰好包含另一个项目("Plastic"),我仍然希望保留这两个项目。因此,大小写可以不同,但该项目必须是字母对字母的匹配,才能将其删除。

必须保留原始列表顺序。应删除该项目的第一个实例之后的所有重复项。应保留该一审的原始案例以及所有非重复项目的原始案例。

我搜索过,只找到了解决一种需求或另一种需求的问题,而不是同时满足两者的问题。

很难用列表理解(或以牺牲清晰度为代价)来编码,因为你需要过滤掉重复项的累积/记忆效应。

也不可能使用set理解,因为它会破坏原始顺序。

带有循环和辅助set的经典方式,您可以在其中存储遇到的字符串的小写版本。仅当小写版本不在集合中时,才将字符串存储在结果列表中

myList = ["paper", "Plastic", "aluminum", "PAPer", "tin", "glass", "tin", "PAPER", "Polypropylene Plastic"]
result=[]
marker = set()
for l in myList:
ll = l.lower()
if ll not in marker:   # test presence
marker.add(ll)
result.append(l)   # preserve order
print(result)

结果:

['paper', 'Plastic', 'aluminum', 'tin', 'glass', 'Polypropylene Plastic']

使用.casefold()而不是.lower()可以处理某些区域设置中微妙的"大小写"差异(例如Strasse/Straße中的德语双"s")。

编辑:可以通过列表理解来做到这一点,但它真的很笨拙:

marker = set()
result = [not marker.add(x.casefold()) and x for x in myList if x.casefold() not in marker]

它在set.addNone输出上使用and来调用此函数(列表理解中的副作用,很少是一件好事......),并且无论如何都会返回x。主要的缺点是:

  • 可读性
  • casefold()被调用两次的事实,一次用于测试,一次用于存储在标记集中
import pandas as pd
df=pd.DataFrame(myList)
df['lower']=df[0].apply(lambda x: x.lower())
df.groupby('lower',sort=0)[0].first().tolist()

输出:

['paper', 'Plastic', 'aluminum', 'tin', 'glass','Polypropylene Plastic']

编辑:好的,我编辑了我的答案,因为问题在此期间发生了变化。 现在,它会检查在原始列表中是否找到大写单词,并在找不到时将其转换为小写。

import string
def custom_filter(my_list):
seen = set()
result_list = []
for i in my_list:
item = string.capwords(i)
if item not in my_list:
item = item.lower()
if item not in seen:
result_list.append(item)
seen.add(item)
return result_list

print(custom_filter(myList))

输出:

['paper', 'Plastic', 'aluminum', 'tin', 'glass', 'Polypropylene Plastic']
mydict = {}
myList = ["paper", "Plastic", "aluminum", "tin", "glass", "Polypropylene Plastic"]
mynewList = []
for elem in myList:
if elem.lower() in mydict:
continue
else:
mydict[elem.lower()] = elem.lower()
mynewList.append(elem)
print(mynewList)

结果['paper', 'Plastic', 'aluminum', 'tin', 'glass', 'Polypropylene Plastic']

基本上,与@Jean-弗朗索瓦·法布尔的第一个答案相同,但使用字典。

另一种使用collections.defaultdict的方法

from collections import defaultdict
myList = ["paper", "Plastic", "aluminum", "PAPer", "tin", "glass", "tin", "PAPER", "Polypropylene Plastic"]
d_dict = defaultdict(list)
for k,v in enumerate(myList):
d_dict[v.lower()].append(k)
[myList[j] for j in sorted(i[0] for i in d_dict.values())]

输出

['paper', 'Plastic', 'aluminum', 'tin', 'glass', 'Polypropylene Plastic']

我发现@Gábor Fekete的答案非常好。 以下是他方法的延续:

myList = ["paper", "Plastic", "aluminum", "PAPer", "tin", "glass",
"tin", "PAPER", "Polypropylene Plastic"]
def is_already_in(value, used_elements):
low = value.lower()
if low in used_elements:
return True
used_elements.add(low)
return False
used_elements = set()
print([ e for e in myList if not is_already_in(e, used_elements) ])

最新更新