2017-07-14 127 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的矫枉过正,但可能对其他人非常有用。 :) –