Подтвердить что ты не робот

Поиск в словаре без учета регистра?

Я могу использовать карту для реализации поиска с учетом регистра без учета регистра с помощью Python.

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

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

Как я могу сделать то же самое со словарем?

Я попробовал следующий код, но ap имеет список ['a', 'b', 'c'], а не словарь, нечувствительный к регистру.

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

Ответ 1

Обратите внимание, что создание словаря без учета регистра, каким бы то ни было образом, может потерять информацию: например, как бы вы "не учитывали регистр" {'a': 23, 'A': 45}?! Если вам все равно, где ключ находится в dict или нет (то есть, не заботятся о том, какое значение соответствует ему), тогда вместо set сделайте set.

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

(в каждой версии Python или {k.lower() for k in thedict}, если вы довольны тем, что ваш код работает только в Python 2.7 или новее для какого-то чисто декоративного синтаксического сахара;-), и проверьте с помощью if k.lower() in theset: ....

Или вы можете создать класс-оболочку, например, возможно, только для чтения...:

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

Это будет сохраняться (без фактического изменения оригинального словаря, поэтому всякая точная информация по-прежнему может быть извлечена для него, если и когда потребуется) произвольное одно из возможных значений для ключей, которые "сворачиваются" в один ключ из-за без ограничений по строкам и предлагать все доступные словари только для чтения (только с строковыми ключами) плюс метод actual_key_case, возвращающий фактический пример, используемый для любого заданного строкового ключа (или None, если не изменяется случай данный строковый ключ соответствует любому ключу в словаре).

Ответ 2

Использование понятий dict (Python2.7 +)

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

Если ваш питон слишком стар для понимания dict

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

затем найдите значение с помощью строчной версии ключа

value = a_lower[key.lower()]

Ответ 3

Начните использовать реальный нечувствительный к регистру словарь с помощью:

from requests.structures import CaseInsensitiveDict

Или, если вы хотите увидеть код:

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

Ответ 4

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

сделает то, что вы ищете.

map (function, iterable) работает над итерируемым; и итерабельность словаря - это список ключей.

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

zip объединяет ключи и значения обратно в пары, но как ряд кортежей. dict преобразует кортежи обратно в dict.

Вы также можете сделать что-то вроде

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}

Или, еще веселее...

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

Ответ 5

Если вам не нужен поиск очень часто, вы можете использовать эту функцию, не теряя пространства для другой копии словаря. Он медленный, хотя все ключи должны проверяться каждый раз.

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'

Ответ 6

Просто хотел добавить __setitem__, pop to 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())