Как удалить дубликаты без учета регистра из списка при сохранении исходного порядка списка?

у меня есть список строк, таких как:

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