Как удалить дубликаты без учета регистра из списка при сохранении исходного порядка списка?
у меня есть список строк, таких как:
myList = ["paper", "Plastic", "aluminum", "PAPer", "tin", "glass", "tin", "PAPER", "Polypropylene Plastic"]
Я хочу этот результат (и это единственный приемлемый результат):
myList = ["paper", "Plastic", "aluminum", "tin", "glass", "Polypropylene Plastic"]
обратите внимание, что если элемент ("Polypropylene Plastic"
) содержит другой элемент ("Plastic"
), Я все-таки хотел бы сохранить оба. Таким образом, случаи могут быть разными, но элемент должен соответствовать букве за буквой, чтобы его можно было удалить.
исходный порядок списка должен быть сохранен. Все дубликаты после первого экземпляра из этого пункта следует удалить. Исходный случай этого первого экземпляра должен быть сохранен, а также исходные случаи всех не повторяющихся элементов.
Я искал и нашел только вопросы, которые касаются одной потребности или другой, а не обоих.
6 ответов
трудно кодировать это с пониманием списка (или за счет ясности) из-за эффекта накопления/памяти, который вам нужно отфильтровать дубликаты.
также невозможно использовать 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()
позволяет обрабатывать тонкие" кожух " различия в некоторых местах (например, немецкий двойной "s" в Штрассе/Штрассе).
Edit: it is можно сделать это с пониманием списка, но это действительно хаки:
marker = set()
result = [not marker.add(x.casefold()) and x for x in myList if x.casefold() not in marker]
используется and
на None
выход set.add
для вызова этой функции (побочный эффект в постижении список , редко что-то хорошее...), и возвратить 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']
EDIT: хорошо, я отредактировал свой ответ, поскольку вопрос тем временем изменился. Теперь он проверяет, Найдено ли заглавное слово в исходном списке и преобразует его в нижний регистр, когда оно не найдено.
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-François Fabre, но с использованием словаря.
еще один способ, с помощью 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 довольно хорошим. Вот продолжение его подхода:--2-->
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) ])