Bir sözlükteki değeri bilerek bir anahtar bulmanın doğrudan bir yolu var mı?
Tek düşünebildiğim şu:
key = [key for key, value in dict_obj.items() if value == 'value'][0]
Bir sözlükteki değeri bilerek bir anahtar bulmanın doğrudan bir yolu var mı?
Tek düşünebildiğim şu:
key = [key for key, value in dict_obj.items() if value == 'value'][0]
iteritems
benim için kullanmıyor , bu 40 kat daha hızlı bir fark yaratıyor ... () .sonraki yöntemi kullanarak
reverse_dictionary = {v:k for k,v in dictionary.items()}
Yanıtlar:
Hiçbiri yok. Değerin 0 veya 1'den fazla dahil olmak üzere herhangi bir sayıda anahtar üzerinde bulunabileceğini unutmayın.
</sigh>
Listenizi anlamanız, tüm eşleşmeleri bulan tüm emir öğelerini gözden geçirir, ardından yalnızca ilk anahtarı döndürür. Bu oluşturucu ifadesi, yalnızca ilk değeri döndürmek için gerektiği kadar yinelenecektir:
key = next(key for key, value in dd.items() if value == 'value')
dd
dikte nerede . Eşleşme StopIteration
bulunmazsa yükselir, bu yüzden onu yakalamak ve ValueError
veya gibi daha uygun bir istisna döndürmek isteyebilirsiniz KeyError
.
keys = { key for key,value in dd.items() if value=='value' }
birkaç eşleşme varsa tüm anahtarların setini almak için.
Bir sözlüğün tek olduğu durumlar vardır: tek eşleme
Örneğin,
d = {1: "one", 2: "two" ...}
Yalnızca tek bir arama yapıyorsanız yaklaşımınız uygundur. Bununla birlikte, birden fazla arama yapmanız gerekiyorsa, ters bir sözlük oluşturmak daha verimli olacaktır.
ivd = {v: k for k, v in d.items()}
Aynı değere sahip birden fazla anahtar olasılığı varsa, bu durumda istenen davranışı belirlemeniz gerekecektir.
Python 2.6 veya daha eskiyse, şunu kullanabilirsin:
ivd = dict((v, k) for k, v in d.items())
ivd=dict([(v,k) for (k,v) in d.items()])
invd = { v:k for k,v in d.items() }
Bu sürüm sizinkinden % 26 daha kısadır ancak gereksiz / belirsiz değerler için bile aynı şekilde çalışır (sizinki gibi ilk eşleşmeyi döndürür). Ancak, muhtemelen sizinkinden iki kat daha yavaştır, çünkü iki kez dikteden bir liste oluşturur.
key = dict_obj.keys()[dict_obj.values().index(value)]
Ya da kısalığı okunabilirliğe tercih ederseniz, bir karakter daha kaydedebilirsiniz.
key = list(dict_obj)[dict_obj.values().index(value)]
Verimliliği tercih ederseniz, @ PaulMcGuire'ın yaklaşımı daha iyidir. Aynı değeri paylaşan çok sayıda anahtar varsa, bu anahtar listesini bir liste anlayışıyla başlatmamak ve bunun yerine bir oluşturucu kullanmak daha etkilidir:
key = (key for key, value in dict_obj.items() if value == 'value').next()
dict.keys()
ve aramalar arasında mutasyona uğramadığı dict.values()
sürece karşılık geleceği garantilidir dict
.
Bu hala çok alakalı olduğu için, ilk Google isabeti ve bunu çözmek için biraz zaman harcıyorum, (Python 3'te çalışma) çözümümü göndereceğim:
testdict = {'one' : '1',
'two' : '2',
'three' : '3',
'four' : '4'
}
value = '2'
[key for key in testdict.items() if key[1] == value][0][0]
Out[1]: 'two'
Size eşleşen ilk değeri verecektir.
DoubleDict
Aşağıdakiler gibi sözlüğe benzeyen bir sınıf olabilir mi? Sağlanan metasınıflardan herhangi birini herhangi bir metasınıf ile birlikte kullanabilir DoubleDict
veya kullanmaktan kaçınabilirsiniz.
import functools
import threading
################################################################################
class _DDChecker(type):
def __new__(cls, name, bases, classdict):
for key, value in classdict.items():
if key not in {'__new__', '__slots__', '_DoubleDict__dict_view'}:
classdict[key] = cls._wrap(value)
return super().__new__(cls, name, bases, classdict)
@staticmethod
def _wrap(function):
@functools.wraps(function)
def check(self, *args, **kwargs):
value = function(self, *args, **kwargs)
if self._DoubleDict__forward != \
dict(map(reversed, self._DoubleDict__reverse.items())):
raise RuntimeError('Forward & Reverse are not equivalent!')
return value
return check
################################################################################
class _DDAtomic(_DDChecker):
def __new__(cls, name, bases, classdict):
if not bases:
classdict['__slots__'] += ('_DDAtomic__mutex',)
classdict['__new__'] = cls._atomic_new
return super().__new__(cls, name, bases, classdict)
@staticmethod
def _atomic_new(cls, iterable=(), **pairs):
instance = object.__new__(cls, iterable, **pairs)
instance.__mutex = threading.RLock()
instance.clear()
return instance
@staticmethod
def _wrap(function):
@functools.wraps(function)
def atomic(self, *args, **kwargs):
with self.__mutex:
return function(self, *args, **kwargs)
return atomic
################################################################################
class _DDAtomicChecker(_DDAtomic):
@staticmethod
def _wrap(function):
return _DDAtomic._wrap(_DDChecker._wrap(function))
################################################################################
class DoubleDict(metaclass=_DDAtomicChecker):
__slots__ = '__forward', '__reverse'
def __new__(cls, iterable=(), **pairs):
instance = super().__new__(cls, iterable, **pairs)
instance.clear()
return instance
def __init__(self, iterable=(), **pairs):
self.update(iterable, **pairs)
########################################################################
def __repr__(self):
return repr(self.__forward)
def __lt__(self, other):
return self.__forward < other
def __le__(self, other):
return self.__forward <= other
def __eq__(self, other):
return self.__forward == other
def __ne__(self, other):
return self.__forward != other
def __gt__(self, other):
return self.__forward > other
def __ge__(self, other):
return self.__forward >= other
def __len__(self):
return len(self.__forward)
def __getitem__(self, key):
if key in self:
return self.__forward[key]
return self.__missing_key(key)
def __setitem__(self, key, value):
if self.in_values(value):
del self[self.get_key(value)]
self.__set_key_value(key, value)
return value
def __delitem__(self, key):
self.pop(key)
def __iter__(self):
return iter(self.__forward)
def __contains__(self, key):
return key in self.__forward
########################################################################
def clear(self):
self.__forward = {}
self.__reverse = {}
def copy(self):
return self.__class__(self.items())
def del_value(self, value):
self.pop_key(value)
def get(self, key, default=None):
return self[key] if key in self else default
def get_key(self, value):
if self.in_values(value):
return self.__reverse[value]
return self.__missing_value(value)
def get_key_default(self, value, default=None):
return self.get_key(value) if self.in_values(value) else default
def in_values(self, value):
return value in self.__reverse
def items(self):
return self.__dict_view('items', ((key, self[key]) for key in self))
def iter_values(self):
return iter(self.__reverse)
def keys(self):
return self.__dict_view('keys', self.__forward)
def pop(self, key, *default):
if len(default) > 1:
raise TypeError('too many arguments')
if key in self:
value = self[key]
self.__del_key_value(key, value)
return value
if default:
return default[0]
raise KeyError(key)
def pop_key(self, value, *default):
if len(default) > 1:
raise TypeError('too many arguments')
if self.in_values(value):
key = self.get_key(value)
self.__del_key_value(key, value)
return key
if default:
return default[0]
raise KeyError(value)
def popitem(self):
try:
key = next(iter(self))
except StopIteration:
raise KeyError('popitem(): dictionary is empty')
return key, self.pop(key)
def set_key(self, value, key):
if key in self:
self.del_value(self[key])
self.__set_key_value(key, value)
return key
def setdefault(self, key, default=None):
if key not in self:
self[key] = default
return self[key]
def setdefault_key(self, value, default=None):
if not self.in_values(value):
self.set_key(value, default)
return self.get_key(value)
def update(self, iterable=(), **pairs):
for key, value in (((key, iterable[key]) for key in iterable.keys())
if hasattr(iterable, 'keys') else iterable):
self[key] = value
for key, value in pairs.items():
self[key] = value
def values(self):
return self.__dict_view('values', self.__reverse)
########################################################################
def __missing_key(self, key):
if hasattr(self.__class__, '__missing__'):
return self.__missing__(key)
if not hasattr(self, 'default_factory') \
or self.default_factory is None:
raise KeyError(key)
return self.__setitem__(key, self.default_factory())
def __missing_value(self, value):
if hasattr(self.__class__, '__missing_value__'):
return self.__missing_value__(value)
if not hasattr(self, 'default_key_factory') \
or self.default_key_factory is None:
raise KeyError(value)
return self.set_key(value, self.default_key_factory())
def __set_key_value(self, key, value):
self.__forward[key] = value
self.__reverse[value] = key
def __del_key_value(self, key, value):
del self.__forward[key]
del self.__reverse[value]
########################################################################
class __dict_view(frozenset):
__slots__ = '__name'
def __new__(cls, name, iterable=()):
instance = super().__new__(cls, iterable)
instance.__name = name
return instance
def __repr__(self):
return 'dict_{}({})'.format(self.__name, list(self))
Hayır, tüm anahtarlara bakmadan ve tüm değerlerini kontrol etmeden bunu verimli bir şekilde yapamazsınız. Yani bunu O(n)
yapmak için zamana ihtiyacın olacak . Bu tür çok sayıda arama yapmanız gerekiyorsa, bunu tersine çevrilmiş bir sözlük oluşturarak (içinde de yapılabilir O(n)
) ve ardından bu ters çevrilmiş sözlüğün içinde bir arama yaparak (her arama ortalama olarakO(1)
) .
Normal bir sözlükten tersine çevrilmiş bir sözlüğün nasıl oluşturulacağına dair bir örnek (birden çoğa eşleme yapabilecek):
for i in h_normal:
for j in h_normal[i]:
if j not in h_reversed:
h_reversed[j] = set([i])
else:
h_reversed[j].add(i)
Örneğin
h_normal = {
1: set([3]),
2: set([5, 7]),
3: set([]),
4: set([7]),
5: set([1, 4]),
6: set([1, 7]),
7: set([1]),
8: set([2, 5, 6])
}
senin h_reversed
olacaksın
{
1: set([5, 6, 7]),
2: set([8]),
3: set([1]),
4: set([5]),
5: set([8, 2]),
6: set([8]),
7: set([2, 4, 6])
}
Bildiğim kadarıyla bir tane yok, ancak bunu yapmanın bir yolu, anahtara göre normal arama için bir dikte ve değere göre ters arama için başka bir dikt oluşturmaktır.
Burada böyle bir uygulamanın bir örneği var:
http://code.activestate.com/recipes/415903-two-dict-classes-which-can-lookup-keys-by-value-an/
Bu, bir değer için anahtarların aranmasının, basit bir liste olarak döndürülebilecek birden çok sonuçla sonuçlanabileceği anlamına gelir.
Bunun 'savurgan' olarak değerlendirilebileceğini biliyorum, ancak bu senaryoda anahtarı genellikle değer kaydında ek bir sütun olarak saklıyorum:
d = {'key1' : ('key1', val, val...), 'key2' : ('key2', val, val...) }
bu bir değiş tokuş ve yanlış hissediyor, ancak basit ve işe yarıyor ve elbette değerlerin basit değerlerden ziyade tekil olmasına bağlı.
reverse_dictionary = {v:k for k,v in dictionary.items()}
Yapacak çok fazla ters aramanız varsa
# oneline solution using zip
>> x = {'a':100, 'b':999}
>> y = dict(zip(x.values(), x.keys()))
>> y
{100: 'a', 999: 'b'}
Sözlükteki değerler aracılığıyla herhangi bir nesne olabilir, hashing uygulanamaz veya başka şekilde dizine eklenemez. Bu nedenle, değere göre anahtarı bulmak bu koleksiyon türü için doğal değildir. Bunun gibi herhangi bir sorgu yalnızca O (n) zamanında yürütülebilir. Dolayısıyla, bu sık sık yapılan bir görevse, Jon sujjested gibi bir anahtar indekslemesine veya hatta bazı uzamsal indekslere (DB veya http://pypi.python.org/pypi/Rtree/ ) göz atmalısınız .
Sözlükleri bir tür "veritabanı" olarak kullanıyorum, bu nedenle yeniden kullanabileceğim bir anahtar bulmam gerekiyor. Benim durumum için, bir anahtarın değeri ise None
, o zaman onu alıp başka bir kimliği "tahsis etmek" zorunda kalmadan yeniden kullanabilirim. Paylaşacağımı düşündüm.
db = {0:[], 1:[], ..., 5:None, 11:None, 19:[], ...}
keys_to_reallocate = [None]
allocate.extend(i for i in db.iterkeys() if db[i] is None)
free_id = keys_to_reallocate[-1]
Bunu beğendim çünkü StopIteration
veya gibi herhangi bir hatayı yakalamam gerekmiyor IndexError
. Kullanılabilir bir anahtar varsa, o free_id
zaman bir tane içerir. Eğer yoksa, o zaman basitçe olacaktır None
. Muhtemelen pitonik değil, ama gerçekten try
burada kullanmak istemedim ...