มีวิธีที่ตรงไปตรงมาในการค้นหาคีย์โดยการรู้ค่าในพจนานุกรมหรือไม่?
ทั้งหมดที่ฉันคิดได้คือ:
key = [key for key, value in dict_obj.items() if value == 'value'][0]
มีวิธีที่ตรงไปตรงมาในการค้นหาคีย์โดยการรู้ค่าในพจนานุกรมหรือไม่?
ทั้งหมดที่ฉันคิดได้คือ:
key = [key for key, value in dict_obj.items() if value == 'value'][0]
iteritems
สำหรับฉันสิ่งนี้ทำให้ความแตกต่างเร็วขึ้น 40 เท่า ... โดยใช้วิธี () .next
reverse_dictionary = {v:k for k,v in dictionary.items()}
คำตอบ:
ไม่มีเลย อย่าลืมว่าค่านี้อาจพบได้ในจำนวนคีย์ใด ๆ รวมทั้ง 0 หรือมากกว่า 1
</sigh>
ความเข้าใจในรายการของคุณจะตรวจสอบรายการทั้งหมดของ dict เพื่อค้นหารายการที่ตรงกันทั้งหมดจากนั้นส่งคืนคีย์แรก นิพจน์ตัวสร้างนี้จะวนซ้ำเท่าที่จำเป็นเพื่อส่งคืนค่าแรก:
key = next(key for key, value in dd.items() if value == 'value')
ที่dd
เป็นบริการพจนานุกรม จะยกStopIteration
ถ้าการแข่งขันไม่พบดังนั้นคุณอาจต้องการที่จะจับที่และกลับมาเป็นข้อยกเว้นที่เหมาะสมมากขึ้นเช่นหรือValueError
KeyError
keys = { key for key,value in dd.items() if value=='value' }
ได้รับชุดของคีย์ทั้งหมดหากมีการแข่งขันหลายรายการ
มีหลายกรณีที่พจนานุกรมเป็นแบบหนึ่ง: หนึ่งการแมป
เช่น,
d = {1: "one", 2: "two" ...}
แนวทางของคุณใช้ได้ถ้าคุณทำการค้นหาเพียงครั้งเดียว อย่างไรก็ตามหากคุณจำเป็นต้องทำการค้นหามากกว่าหนึ่งรายการการสร้างพจนานุกรมผกผันจะมีประสิทธิภาพมากกว่า
ivd = {v: k for k, v in d.items()}
หากมีความเป็นไปได้ที่จะมีหลายคีย์ที่มีค่าเดียวกันคุณจะต้องระบุลักษณะการทำงานที่ต้องการในกรณีนี้
หาก Python ของคุณมีอายุ 2.6 หรือเก่ากว่าคุณสามารถใช้ไฟล์
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() }
เวอร์ชันนี้สั้นกว่าของคุณ 26% แต่ทำงานเหมือนกันแม้จะเป็นค่าที่ซ้ำซ้อน / ไม่ชัดเจน (ส่งคืนการจับคู่ครั้งแรกตามที่คุณทำ) อย่างไรก็ตามมันอาจจะช้ากว่าของคุณถึงสองเท่าเพราะมันสร้างรายการจาก dict สองครั้ง
key = dict_obj.keys()[dict_obj.values().index(value)]
หรือหากคุณต้องการความกะทัดรัดมากกว่าความสามารถในการอ่านคุณสามารถบันทึกอักขระได้อีกหนึ่งตัวด้วย
key = list(dict_obj)[dict_obj.values().index(value)]
และถ้าคุณต้องการประสิทธิภาพของ @ PaulMcGuire's แนวทางจะดีกว่า หากมีคีย์จำนวนมากที่ใช้ค่าเดียวกันจะมีประสิทธิภาพมากกว่าที่จะไม่สร้างอินสแตนซ์รายการคีย์นั้นด้วยความเข้าใจรายการและใช้ตัวสร้างแทน:
key = (key for key, value in dict_obj.items() if value == 'value').next()
dict.keys()
และdict.values()
รับประกันว่าจะสอดคล้องกันตราบเท่าที่dict
ไม่มีการกลายพันธุ์ระหว่างการโทร
เนื่องจากสิ่งนี้ยังคงมีความเกี่ยวข้องมาก Google hit แรกและฉันใช้เวลาพอสมควรในการหาสิ่งนี้ฉันจะโพสต์โซลูชัน (ทำงานใน Python 3) ของฉัน:
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'
จะให้ค่าแรกที่ตรงกัน
บางทีคลาสที่เหมือนพจนานุกรมเช่นDoubleDict
ด้านล่างคือสิ่งที่คุณต้องการ? คุณสามารถใช้ metaclasses ใดก็ได้ที่ให้มาในการเชื่อมต่อกับDoubleDict
หรืออาจหลีกเลี่ยงการใช้ metaclass ใด ๆ เลยก็ได้
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))
ไม่ได้คุณไม่สามารถทำได้อย่างมีประสิทธิภาพโดยไม่ต้องดูคีย์ทั้งหมดและตรวจสอบค่าทั้งหมด ดังนั้นคุณจะต้องใช้O(n)
เวลาในการทำสิ่งนี้ หากคุณต้องการค้นหาจำนวนมากคุณจะต้องดำเนินการนี้อย่างมีประสิทธิภาพโดยการสร้างพจนานุกรมแบบย้อนกลับ (สามารถทำได้ในO(n)
) จากนั้นทำการค้นหาภายในพจนานุกรมที่กลับรายการนี้ (การค้นหาแต่ละครั้งจะใช้เวลาโดยเฉลี่ยO(1)
)
นี่คือตัวอย่างของวิธีการสร้างพจนานุกรมแบบย้อนกลับ (ซึ่งจะสามารถทำการแมปหนึ่งถึงหลาย ๆ รายการได้) จากพจนานุกรมปกติ:
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)
ตัวอย่างเช่นหากไฟล์
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])
}
คุณh_reversed
จะเป็น
{
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])
}
ไม่มีวิธีใดเท่าที่ฉันรู้วิธีหนึ่งที่ทำได้คือการสร้างคำสั่งสำหรับการค้นหาตามปกติโดยใช้คีย์และอีกคำสั่งหนึ่งสำหรับการค้นหาแบบย้อนกลับตามค่า
มีตัวอย่างของการใช้งานดังกล่าวที่นี่:
http://code.activestate.com/recipes/415903-two-dict-classes-which-can-lookup-keys-by-value-an/
นี่หมายความว่าการค้นหาคีย์สำหรับค่าอาจทำให้ได้ผลลัพธ์หลายรายการซึ่งสามารถส่งคืนเป็นรายการแบบง่าย
ฉันรู้ว่านี่อาจถือเป็น 'สิ้นเปลือง' แต่ในสถานการณ์นี้ฉันมักจะเก็บคีย์ไว้เป็นคอลัมน์เพิ่มเติมในเรกคอร์ดค่า:
d = {'key1' : ('key1', val, val...), 'key2' : ('key2', val, val...) }
มันเป็นการแลกเปลี่ยนและรู้สึกผิด แต่มันง่ายและใช้งานได้จริงและแน่นอนว่าขึ้นอยู่กับค่าที่เป็นสิ่งที่เพิ่มขึ้นมากกว่าค่าง่ายๆ
reverse_dictionary = {v:k for k,v in dictionary.items()}
หากคุณมีการค้นหาแบบย้อนกลับจำนวนมากที่ต้องทำ
# oneline solution using zip
>> x = {'a':100, 'b':999}
>> y = dict(zip(x.values(), x.keys()))
>> y
{100: 'a', 999: 'b'}
ด้วยค่าในพจนานุกรมอาจเป็นวัตถุประเภทใดก็ได้ที่ไม่สามารถแฮชหรือจัดทำดัชนีด้วยวิธีอื่นได้ ดังนั้นการค้นหาคีย์ด้วยค่าจึงไม่เป็นธรรมชาติสำหรับคอลเล็กชันประเภทนี้ แบบสอบถามใด ๆ เช่นนั้นสามารถดำเนินการได้ในเวลา O (n) เท่านั้น ดังนั้นหากเป็นงานบ่อยคุณควรมองหาการสร้างดัชนีของคีย์เช่น Jon sujjested หรือแม้แต่ดัชนีเชิงพื้นที่ (DB หรือhttp://pypi.python.org/pypi/Rtree/ )
ฉันใช้พจนานุกรมเป็น "ฐานข้อมูล" ประเภทหนึ่งดังนั้นฉันจึงต้องหาคีย์ที่สามารถนำมาใช้ใหม่ได้ สำหรับกรณีของฉันถ้าค่าของคีย์คือNone
ฉันสามารถนำไปใช้ซ้ำได้โดยไม่ต้อง "จัดสรร" id อื่น แค่คิดว่าฉันจะแบ่งปัน
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]
ฉันเช่นนี้เพราะผมไม่ต้องพยายามและจับข้อผิดพลาดใด ๆ เช่นหรือStopIteration
IndexError
หากมีคีย์พร้อมใช้งานก็free_id
จะมีคีย์ None
ถ้ามีไม่ได้แล้วมันก็จะเป็น อาจจะไม่ใช่ pythonic แต่ฉันไม่อยากใช้try
ที่นี่ ...