2010-07-21 5 views
19

Je peux utiliser map pour implémenter la recherche de liste insensible à la casse avec Python.Recherche de dictionnaire insensible à la casse?

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

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

Comment faire la même chose avec un dictionnaire?

J'ai essayé le code suivant, mais ap a la liste de ['a', 'b', 'c'], pas le dictionnaire insensible à la casse.

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

Voulez-vous des solutions explicites? J'ai utilisé la carte --- c'est comme ça que j'ai lu la question la première fois. –

+1

Voir [PEP-455] (https://www.python.org/dev/peps/pep-0455/): ceci est prévu pour l'inclusion de la bibliothèque standard dans Python 3.5 (comme 'collections.TransformDict', à condition que la transformation soit 'str.casefold' ou similaire) –

+0

[PEP-455 a finalement été rejetée.] (https://www.python.org/dev/peps/pep-0455/#rejection) –

Répondre

30

Notez que faire un dictionnaire insensible à la casse, par quelque moyen que, pourrait bien perdre des informations: par exemple, comment voulez-vous « Case- insensibiliser "{'a': 23, 'A': 45}?! Si tout ce que vous aimez est là une clé est dans le dict ou non (autrement dit, ne se soucient pas de ce que la valeur correspond à elle), puis faire une place set - à savoir

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

(dans toutes les versions de Python , ou {k.lower() for k in thedict} si vous êtes satisfait de votre code fonctionnant uniquement en Python 2.7 ou version ultérieure pour des raisons de syntaxe purement décorative sucre ;-), et vérifiez avec if k.lower() in theset: ....

Ou, vous pourriez faire une classe d'emballage, par exemple, peut-être une lecture seule ...:

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

Cela permet de garder (sans réellement altérer le dictionnaire original, donc toutes les informations précises peut encore être récupérer pour cela, si et quand nécessaire) une valeur arbitraire de valeurs possibles multiples pour les clés qui "s'effondrent" en une seule clé en raison de l'insensibilité à la casse, et offre toutes les méthodes de dictionnaires en lecture seule (avec les clés de chaîne, uniquement) plus une méthode actual_key_case renvoyant le mélange de cas réel utilisé pour une clé de chaîne donnée (ou None si aucune modification de cas de cette clé de chaîne donnée ne correspond à une clé dans le dictionnaire).

+0

Très bien - cela a résolu un problème que j'étais lorsque l'API faisait une correspondance insensible à la casse sur un nom de champ demandé, mais en retournant le nom du champ canonique, je demanderais 'email', mais j'obtiendrais 'Email'. Cette dict m'a permis de mapper cela au nom de domaine que j'ai demandé. Résultat! – metadaddy

+2

petite faute de frappe dans la méthode __getitem __(). self._s au lieu de self_s. Apparemment, je ne peux pas faire une édition de 1 caractère dans SO (doit être> = 6) !! – SteveJ

+1

Pas tout à fait un remplacement direct pour un dictionnaire, voir un complet à http://stackoverflow.com/a/27890005/99834 – sorin

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

fera ce que vous cherchez.

carte (fonction, itérable) fonctionne sur l'itérable; et itérable du dictionnaire est la liste des clés. Zip regroupe les clés et les valeurs de retour en paires, mais sous la forme d'une série de n-uplets. dict convertit les tuples en dict.

Vous pouvez aussi faire quelque chose comme

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} 

Ou, encore plus de plaisir ...

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

Utilisation dict compréhensions (python2.7 +)

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

Si votre python est trop vieux pour comprendre les dictées

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

puis rechercher la valeur avec la version minuscule de la clé

value = a_lower[key.lower()] 
3

Si vous n'avez pas besoin de la recherche très souvent, vous pouvez utiliser cette fonction sans perdre de place pour une autre copie du dictionnaire.C'est lent cependant que toutes les clés doivent être vérifiées contre chaque fois.

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

Je voulais juste ajouter __setitem__, pop à Alex Martelli Réponse:

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

Commencez à utiliser un cas réel dictionnaire insensible via:

from requests import CaseInsensitiveDict 

Ou si vous voulez voir le code :

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

Il est actuellement dans requests.structures maintenant: http://docs.python-requests.org /fr/v0.5.0/api/#structures – Gallaecio