2017-07-14 67 views
1

我有一個從多個列表生成的列表。此組合列表包含由最終用戶生成的名稱。因此包含相似的名稱,但具有不同的大寫/小寫字符。 我想過濾出包含相同字符的名稱,並只保留原始列表中的第一個。對Python中的集合不區分大小寫

舉個例子,我有以下列表:

L0 = ['A_B Cdef', 'A_B Cdef', 'A_B Cdef', 'A_B CdEF', 'A_B CDEF','a_B CdEF', 'A_b CDEF', 'GG_ooo', 'a1-23456'] 

如果我運行:

L1 = list(set(L0)) 

我得到:

['a1-23456', 'A_B Cdef', 'A_B CdEF', 'A_B CDEF', 'a_B CdEF', 'A_b CDEF', 'GG_ooo'] 

我想只保留第一具有相同字符的名稱。

所以我的結果是:

['a1-23456', 'A_B Cdef', 'GG_ooo'] 

如果我使用.lower().upper()我得到的名單,但名稱側/上側套管。

我只是想消除「重複」而不考慮大小寫敏感的方法。

非常感謝。

謝謝!

+0

[相關](https://stackoverflow.com/questions/24983172/how-to-eliminate-duplicate-list-entries-in-python-while-preserving-case-sensitiv) – Wondercricket

回答

2

使用散列代替,我不認爲你可以很容易地完成集合。

L0 = {value.lower(): value for value in L0[::-1]}.values() 
+0

謝謝大家的回答!我認爲這是編碼量最低的一個。 – alc

+2

它可能是編碼量最少的編碼,但@ PM2Ring指出,這也是錯誤的,因爲你說'只保留第一個發現'。這保持了最後的發現。 –

+1

@TomWyllie代碼已經更新,可以向後掃描源列表,所以現在它達到了預期的目標。 –

3

您可以使用一組跟蹤.lower()版本的值,然後將原來的數值附加到一個新的列表,如果他們.lower()版本是不是已經在集:

s = set() 
L = [] 
for x in L0: 
    if x.lower() not in s: 
     s.add(x.lower()) 
     L.append(x) 

print(L) 
# ['A_B Cdef', 'GG_ooo', 'a1-23456'] 
+0

該死的打敗了​​我;) –

1

如果您想要按照規則玩耍,我能想到的最好的解決方案有點混亂,用集合來跟蹤哪些詞已經出現;

seen_words = set() 
L1 = [] 
for word in L0: 
    if word.lower() not in seen_words: 
     L1.append(word) 
     seen_words.add(word.lower()) 

如果你想獲得一點點hackier還有一個更優雅的解決方案,你可以使用字典來跟蹤哪些詞已經看出來了,這是一個幾乎有內襯;

seen_words = {} 
L1 = [seen_words.setdefault(word.lower(), word) 
     for word in L0 if word.lower() not in seen_words] 
print(L1) 

兩種解決方案都輸出相同的結果;

['A_B Cdef', 'GG_ooo', 'a1-23456'] 
+0

可愛,雖然有些人不會贊同你的清單理解與副作用(突變'seen_words')...或使用字典而不是一套純粹如此,您可以使用列表組件而不是「傳統」循環來執行此操作。 ;) –

+0

我最初有一套基於解決方案,但正如你指出的那樣,這裏肯定是一種折衷,我認爲這第二種解決方案更優雅,但更加黑客,這是毫無疑問的。我可能包括這兩個,並給OP實際選擇。 –

+0

當然,第一個版本更長,但它使用更少的內存,並沒有惡作劇的副作用,所以我會將它分類爲更多Pythonic。 –

2

你已經有幾個很好的答案,下面的代碼可能是矯枉過正爲您的使用情況,但只是爲了好玩我創建了一個簡單的區分大小寫的可變集類。請注意,它保留第一個找到的字符串,而不是讓它在稍後的條目中被破壞。

import collections.abc 

class CasefoldSet(collections.abc.MutableSet): 
    def __init__(self, iterable=None): 
     self.elements = {} 
     if iterable is not None: 
      for v in iterable: 
       self.add(v) 

    def __contains__(self, value): 
     return value.casefold() in self.elements 

    def add(self, value): 
     key = value.casefold() 
     if key not in self.elements: 
      self.elements[key] = value 

    def discard(self, value): 
     key = value.casefold() 
     if key in self.elements: 
      del self.elements[key] 

    def __len__(self): 
     return len(self.elements) 

    def __iter__(self): 
     return iter(self.elements.values()) 

    def __repr__(self): 
     return '{' + ', '.join(map(repr, self)) + '}' 

# test 

l0 = [ 
    'GG_ooo', 'A_B Cdef', 'A_B Cdef', 'A_B Cdef', 
    'A_B CdEF', 'A_B CDEF', 'a_B CdEF', 'A_b CDEF', 'a1-23456', 
] 

l1 = CasefoldSet(l0[:4]) 
print(l1) 
l1 |= l0[4:] 
print(l1) 
l2 = {'a', 'b', 'A_B Cdef'} | l1 
print(l2) 
l3 = l2 & {'a', 'GG_ooo', 'a_B CdEF'} 
print(l3) 

輸出

{'GG_ooo', 'A_B Cdef'} 
{'GG_ooo', 'A_B Cdef', 'a1-23456'} 
{'GG_ooo', 'A_B Cdef', 'a1-23456', 'b', 'a'} 
{'a_B CdEF', 'a', 'GG_ooo'} 

該類繼承collections.abc.MutableSet各種有用的方法,但要使它完全取代set它確實需要一些更多的方法。請注意,如果您嘗試將其傳遞給非字符串項目,它將引發AttributeError

+0

這是因爲你指出OP的矯枉過正,但可能對其他人非常有用。 :) –