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

时间:2018-01-16 14:16:17

标签: python list

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

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

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

"Polypropylene Plastic"

请注意,如果某个项目("Plastic")恰好包含其他项目(sharedPrefs),我仍然希望保留这两项。因此,案例可能会有所不同,但该项目必须是字母匹配,以便将其删除。

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

我已经搜索过,只找到了解决一个需求或另一个需求的问题,而不是两者。

6 个答案:

答案 0 :(得分:15)

使用列表理解(或以清晰度为代价)对其进行编码很困难,因为您需要过滤掉重复项的累积/记忆效应。

也无法使用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]

它在and的{​​{1}}输出上使用None来调用此函数(列表理解中的副作用,很少是好事......),并返回{{ 1}}无论如何。主要的不满是:

  • 可读性
  • set.add被调用两次,一次用于测试,一次用于存储在标记集中

答案 1 :(得分:1)

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']

答案 2 :(得分:0)

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

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']

答案 3 :(得分:0)

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-FrançoisFabre的第一个答案相同,但使用字典。

答案 4 :(得分:0)

使用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']

答案 5 :(得分:-1)

我发现@GáborFekete的答案相当不错。以下是他的方法的延续:

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) ])