2010-07-21 13 views
19

Puedo usar el mapa para implementar la búsqueda de lista insensible a mayúsculas y minúsculas con Python.¿Búsqueda del diccionario insensible a mayúsculas y minúsculas?

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

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

¿Cómo puedo hacer lo mismo con el diccionario?

Probé el siguiente código, pero ap tiene la lista de ['a', 'b', 'c'], no el diccionario que no distingue entre mayúsculas y minúsculas.

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

¿Quería soluciones explícitas? utilicé el mapa --- así es como leí la pregunta la primera vez. –

+1

Ver [PEP-455] (https://www.python.org/dev/peps/pep-0455/): está programado para la inclusión de una biblioteca estándar en Python 3.5 (como 'collections.TransformDict', siempre que la transformación sea 'str.casefold' o similar) –

+0

[PEP-455 fue finalmente rechazado.] (https://www.python.org/dev/peps/pep-0455/#rejection) –

Respuesta

30

Tenga en cuenta que hacer un diccionario de mayúsculas y minúsculas, por lo tanto, también puede perder información: por ejemplo, ¿cómo se "caso- insensibilizar "{'a': 23, 'A': 45}?! Si todo lo que importa es que una llave está en la dict o no (es decir, no se preocupan por lo que el valor corresponde a la misma), a continuación, hacer una set lugar - es decir

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

(en todas las versiones de Python o {k.lower() for k in thedict} si está contento con su código que solo funciona en Python 2.7 o posterior por el bien de un azucar de sintaxis puramente decorativa ;-), y consulte con if k.lower() in theset: ....

O, usted podría hacer una clase de contenedor, por ejemplo, tal vez un solo lectura 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()) 

Esto mantendrá (sin llegar a alterar el diccionario original, por lo toda la información precisa todavía puede ser recuperar para ello, si es necesario) un arbitrario de valores posiblemente múltiples para claves que se "colapsan" en una sola tecla debido a la insensibilidad a mayúsculas y minúsculas, y ofrecen todos los métodos de solo lectura de diccionarios (solo con claves de cadena) más un método actual_key_case que devuelve la combinación de casos real utilizada para cualquier clave de cadena determinada (o None si ninguna alteración de mayúsculas o minúsculas de esa clave de serie dada concuerda con ninguna tecla en el diccionario).

+0

Muy bien - esto resolvió un problema que yo estaba teniendo donde una API estaba haciendo una coincidencia insensible a mayúsculas/minúsculas en un nombre de campo solicitado, pero devolviendo el nombre de campo canónico, entonces pedía 'correo electrónico', pero obtengo 'Correo'. Este dict me permitió asignarlo al nombre de campo que pedí. ¡Resultado! – metadaddy

+2

pequeño error tipográfico en el método __getitem __(). self._s en lugar de self_s. Aparentemente no puedo hacer una edición de 1 carácter en SO (debe ser> = 6) !! – SteveJ

+1

No es exactamente un reemplazo directo para un diccionario, vea uno completo en http://stackoverflow.com/a/27890005/99834 – sorin

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

hará lo que usted está buscando.

mapa (función, iterable) funciona sobre el iterable; e iterable del diccionario es la lista de claves.

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

zip reúne las claves y los valores de nuevo en pares, pero como una serie de tuplas. dict convierte las tuplas nuevamente en un dict.

También podría hacer algo como

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} 

O, aún más divertido ...

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

Uso de comprensiones dict (+) Python2.7

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

Si su Python es demasiado viejo para las comprensiones dict

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

continuación, buscar el valor con la versión en minúsculas de la clave

value = a_lower[key.lower()] 
3

Si no necesita la búsqueda muy a menudo, puede utilizar esta función sin perder espacio para otra copia del diccionario.Sin embargo, es lento, ya que todas las claves deben verificarse en todo momento.

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

sólo quería añadir __setitem__, el pop a la contestación de 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

Comenzar a utilizar un caso real diccionario insensibles a través de:

from requests import CaseInsensitiveDict 

O si desea ver el código :

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

Actualmente está en requests.structures now: http://docs.python-requests.org /es/v0.5.0/api/#structures – Gallaecio

Cuestiones relacionadas