2010-07-21 33 views
19

Tôi có thể sử dụng bản đồ để thực hiện tìm kiếm danh sách phân biệt chữ hoa chữ thường với Python.Tìm kiếm từ điển không phân biệt chữ hoa chữ thường?

a = ['xyz', 'wMa', 'Pma']; 

b = map(string.lower, a) 
if 'Xyz'.lower() in b: 
    print 'yes' 

Tôi có thể làm điều tương tự với từ điển bằng cách nào?

Tôi đã thử mã sau, nhưng ap có danh sách ['a', 'b', 'c'], không phải từ điển không phân biệt chữ hoa chữ thường.

a = {'a':1, 'B':2, 'c':3} 
ap = map(string.lower, a) 
+0

Bạn có muốn giải pháp rõ ràng không ly sử dụng bản đồ --- đó là cách tôi đọc câu hỏi lần đầu tiên. –

+1

Xem [PEP-455] (https://www.python.org/dev/peps/pep-0455/): đây là lịch trình đưa thư viện chuẩn vào Python 3.5 (dưới dạng 'collections.TransformDict', với điều kiện biến đổi là 'str.casefold' hoặc tương tự) –

+0

[PEP-455 cuối cùng bị từ chối.] (https://www.python.org/dev/peps/pep-0455/#rejection) –

Trả lời

30

Lưu ý rằng thực hiện một case-insensitive từ điển, bởi bất cứ điều gì có ý nghĩa, cũng có thể bị mất thông tin: ví dụ, sẽ như thế nào bạn "đựng pin- insensitivize "{'a': 23, 'A': 45}?! Nếu tất cả các bạn quan tâm là nơi một mấu chốt là trong dict hay không (tức là, không quan tâm đến những gì giá trị tương ứng với nó), sau đó đưa ra một set thay - tức là

theset = set(k.lower() for k in thedict) 

(trong mọi phiên bản của Python hoặc {k.lower() for k in thedict} nếu bạn hài lòng với mã của mình chỉ hoạt động trong Python 2.7 trở lên vì lợi ích của một số đường cú pháp hoàn toàn trang trí ;-) và kiểm tra với if k.lower() in theset: ....

Hoặc, bạn có thể làm cho một lớp wrapper, ví dụ, có thể là một chỉ đọc một ...:

import collections 

class CaseInsensitiveDict(collections.Mapping): 
    def __init__(self, d): 
     self._d = d 
     self._s = dict((k.lower(), k) for k in d) 
    def __contains__(self, k): 
     return k.lower() in self._s 
    def __len__(self): 
     return len(self._s) 
    def __iter__(self): 
     return iter(self._s) 
    def __getitem__(self, k): 
     return self._d[self._s[k.lower()]] 
    def actual_key_case(self, k): 
     return self._s.get(k.lower()) 

Điều này sẽ giữ (mà không thực sự thay đổi điển ban đầu, vì vậy tất cả thông tin chính xác vẫn có thể được truy xuất nó, nếu và khi cần thiết) một giá trị có thể có nhiều giá trị cho các khóa "thu gọn" thành một khóa duy nhất do không phân biệt chữ hoa chữ thường và cung cấp tất cả các từ điển chỉ đọc (chỉ với các phím chuỗi) cộng với phương thức actual_key_case trả về kết hợp trường hợp thực tế được sử dụng cho bất kỳ khóa chuỗi cụ thể nào (hoặc None nếu không có sự thay đổi trường hợp của khóa chuỗi đã cho khớp với bất kỳ khóa nào trong từ điển).

+0

Rất hay - điều này đã giải quyết được vấn đề tôi đã có nơi một API đã làm một trận đấu không phân biệt chữ hoa chữ thường trên một tên trường được yêu cầu, nhưng trả lại tên trường chuẩn, vì vậy tôi sẽ hỏi 'email', nhưng nhận 'Email'. Dict này cho phép tôi ánh xạ trở lại tên trường mà tôi đã yêu cầu. Kết quả! – metadaddy

+2

typo nhỏ trong phương thức __getitem __(). self._s thay vì self_s. Rõ ràng tôi không thể thực hiện chỉnh sửa 1 ký tự trong SO (phải> = 6) !! – SteveJ

+1

Không thay thế hoàn toàn cho một từ điển, hãy xem toàn bộ từ điển tại http://stackoverflow.com/a/27890005/99834 – sorin

5
dict(zip(map(string.lower,a.keys()),a.values())) 

sẽ làm những gì bạn đang tìm kiếm.

bản đồ (chức năng, có thể lặp lại) hoạt động trên khả năng lặp lại; và có thể lặp lại của từ điển là danh sách các phím.

a = {'a': 1, 'c': 3, 'B': 2} 
for i in a: 
print a 
# returns a c B 

zip tập hợp các khóa và giá trị lại thành từng cặp, nhưng dưới dạng một loạt bộ dữ liệu. dict chuyển đổi các bộ dữ liệu trở lại thành một dict.

Bạn cũng có thể làm điều gì đó như

def myfunc(t): 
return (string.lower(t[0]),t[1]) 

map(myfunc,a.items()) 
# returns [('a', 1), ('c', 3), ('b', 2) 
dict(map(myfunc,a.items())) 
# returns {'a': 1, 'c': 3, 'b': 2} 

Hoặc, thậm chí nhiều niềm vui ...

dict(map(lambda (key, value):(string.lower(key),value),a.items())) 
12

Sử dụng comprehensions dict (Python2.7 +)

a_lower = {k.lower():v for k,v in a.items()} 

Nếu bạn python quá cũ để có thể đọc dict

a_lower = dict((k.lower(),v) for k,v in a.items()) 

sau đó tìm kiếm các giá trị với phiên bản thường của khóa

value = a_lower[key.lower()] 
3

Nếu bạn không cần tra cứu thường xuyên, bạn có thể sử dụng chức năng này mà không lãng phí dung lượng cho bản sao từ điển khác.Nó là chậm mặc dù tất cả các phím phải được kiểm tra chống lại mọi thời gian.

a = {'xyz':2, 'wMa':8, 'Pma':9} 

## if you do not use many times and/or the dict is very big 

def case_insensitive_key(a,k): 
    k = k.lower() 
    return [a[key] for key in a if key.lower() == k] 

print 'yes' if case_insensitive_key(a,'Xyz') else 'no' 
1

Chỉ muốn thêm __setitem__, pop để trả lời Alex Martelli của:

from collections import Mapping 

class CaseInsensitiveDict(Mapping): 
    def __init__(self, d): 
     self._d = d 
     self._s = dict((k.lower(), k) for k in d) 
    def __contains__(self, k): 
     return k.lower() in self._s 
    def __len__(self): 
     return len(self._s) 
    def __iter__(self): 
     return iter(self._s) 
    def __getitem__(self, k): 
     return self._d[self._s[k.lower()]] 
    def __setitem__(self, k, v): 
     self._d[k] = v 
     self._s[k.lower()] = k 
    def pop(self, k): 
     k0 = self._s.pop(k.lower()) 
     return self._d.pop(k0) 
    def actual_key_case(self, k): 
     return self._s.get(k.lower()) 
8

Bắt đầu sử dụng một trường hợp thực tế từ điển nhạy cảm qua:

from requests import CaseInsensitiveDict 

Hoặc nếu bạn muốn xem mã :

class CaseInsensitiveDict(dict): 

    """Basic case insensitive dict with strings only keys.""" 

    proxy = {} 

    def __init__(self, data): 
     self.proxy = dict((k.lower(), k) for k in data) 
     for k in data: 
      self[k] = data[k] 

    def __contains__(self, k): 
     return k.lower() in self.proxy 

    def __delitem__(self, k): 
     key = self.proxy[k.lower()] 
     super(CaseInsensitiveDict, self).__delitem__(key) 
     del self.proxy[k.lower()] 

    def __getitem__(self, k): 
     key = self.proxy[k.lower()] 
     return super(CaseInsensitiveDict, self).__getitem__(key) 

    def get(self, k, default=None): 
     return self[k] if k in self else default 

    def __setitem__(self, k, v): 
     super(CaseInsensitiveDict, self).__setitem__(k, v) 
     self.proxy[k.lower()] = k 
+5

Nó thực sự đang được yêu cầu. Cấu trúc bây giờ: http://docs.python-requests.org /en/v0.5.0/api/#structures – Gallaecio

Các vấn đề liên quan