2010-07-21 23 views
19

Posso usare la mappa per implementare la ricerca di lista senza distinzione tra maiuscole e minuscole con Python.Ricerca dizionario insensibile al maiuscolo?

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

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

Come posso fare la stessa cosa con il dizionario?

Ho provato il seguente codice, ma ap ha l'elenco di ['a', 'b', 'c'], non il dizionario senza distinzione tra maiuscole e minuscole.

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

Volevi soluzioni che esplicitano Ho usato la mappa --- è così che ho letto la domanda la prima volta. –

+1

Vedere [PEP-455] (https://www.python.org/dev/peps/pep-0455/): questo è pianificato per l'inclusione di una libreria standard in Python 3.5 (come 'collections.TransformDict', a condizione che la trasformazione sia 'str.casefold' o simile) –

+0

[PEP-455 è stato infine respinto.] (https://www.python.org/dev/peps/pep-0455/#rejection) –

risposta

30

Nota che per fare un dizionario-case insensitive, con qualsiasi mezzo, potrebbe perdere le informazioni: per esempio, come è possibile "caso- insensibilizzare "{'a': 23, 'A': 45}?! Se tutto ciò che importa è dove la chiave è nel dict o meno (ad esempio, non si cura di ciò che valore corrisponde ad essa), poi fare una set invece - cioè

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

(in ogni versione di Python , oppure {k.lower() for k in thedict} se sei soddisfatto del tuo codice che funziona solo in Python 2.7 o versioni successive per il gusto di uno zucchero di sintassi puramente decorativo ;-) e controlla con if k.lower() in theset: ....

Oppure, si potrebbe fare una classe wrapper, per esempio, forse una sola lettura uno ...:

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

Ciò manterrà (senza effettivamente modificare il dizionario originale, in modo tutte le informazioni precise può ancora essere recuperare per esso, se e quando necessario) un arbitrario di valori possibilmente multipli per le chiavi che "collassano" in una singola chiave a causa dell'insensibilità del caso e offrono tutti i metodi di sola lettura dei dizionari (con chiavi stringa, solo) più un metodo actual_key_case che restituisce il mix case effettivo utilizzato per qualsiasi chiave stringa specificata (o None se nessuna alterazione del caso di quella chiave stringa corrisponde a nessuna chiave nel dizionario).

+0

Molto bello - questo ha risolto un problema ero avendo dove un'API stava facendo una corrispondenza insensibile alle maiuscole e minuscole su un nome di campo richiesto, ma restituendo il nome del campo canonico, quindi avrei chiesto "email", ma ho ricevuto "Email". Questo dettame mi ha permesso di ricondurlo al nome del campo che ho chiesto. Risultato! – metadaddy

+2

piccolo errore di battitura nel metodo __getitem __(). self._s invece di self_s. Apparentemente non posso fare una modifica di 1 personaggio in SO (deve essere> = 6) !! – SteveJ

+1

Non proprio un rimpiazzo sostitutivo per un dizionario, vedere uno completo su http://stackoverflow.com/a/27890005/99834 – sorin

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

farà quello che stai cercando.

map (funzione, iterabile) funziona su iterabile; e iterabile del dizionario è la lista delle chiavi.

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

zip raggruppa le chiavi e i valori in coppie, ma come una serie di tuple. dettare converte le tuple in un dettato.

Si potrebbe anche fare qualcosa di simile

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} 

Oppure, ancora più divertente ...

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

Uso comprensioni dict (Python2.7 +)

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

Se la vostra python è troppo vecchio per comprendere le parole

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

poi cercare il valore con la versione minuscola della chiave

value = a_lower[key.lower()] 
3

Se non si ha bisogno della ricerca molto spesso è possibile utilizzare questa funzione senza sprecare spazio per altre copie del dizionario.È lento anche se tutte le volte è necessario controllare tutte le chiavi.

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

Volevo solo aggiungere __setitem__, pop alla risposta di Alex Martelli:

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

iniziare a utilizzare un vero e proprio caso dizionario insensitive via:

from requests import CaseInsensitiveDict 

Oppure, se si vuole vedere il codice :

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

Attualmente è in request.structures: http://docs.python-requests.org /en/v0.5.0/api/#structures – Gallaecio

Problemi correlati