I’m doing this switchboard thing in python where I need to keep track of who’s talking to whom, so if Alice –> Bob, then that implies that Bob –> Alice.
Yes, I could populate two hash maps, but I’m wondering if anyone has an idea to do it with one.
Or suggest another data structure.
There are no multiple conversations. Let’s say this is for a customer service call center, so when Alice dials into the switchboard, she’s only going to talk to Bob. His replies also go only to her.
回答 0
您可以通过子类化dict并添加所需的逻辑来创建自己的字典类型。这是一个基本示例:
classTwoWayDict(dict):def __setitem__(self, key, value):# Remove any previous connections with these valuesif key in self:del self[key]if value in self:del self[value]
dict.__setitem__(self, key, value)
dict.__setitem__(self, value, key)def __delitem__(self, key):
dict.__delitem__(self, self[key])
dict.__delitem__(self, key)def __len__(self):"""Returns the number of connections"""return dict.__len__(self)//2
它的工作原理如下:
>>> d =TwoWayDict()>>> d['foo']='bar'>>> d['foo']'bar'>>> d['bar']'foo'>>> len(d)1>>>del d['foo']>>> d['bar']Traceback(most recent call last):File"<stdin>", line 7,in<module>KeyError:'bar'
You can create your own dictionary type by subclassing dict and adding the logic that you want. Here’s a basic example:
class TwoWayDict(dict):
def __setitem__(self, key, value):
# Remove any previous connections with these values
if key in self:
del self[key]
if value in self:
del self[value]
dict.__setitem__(self, key, value)
dict.__setitem__(self, value, key)
def __delitem__(self, key):
dict.__delitem__(self, self[key])
dict.__delitem__(self, key)
def __len__(self):
"""Returns the number of connections"""
return dict.__len__(self) // 2
And it works like so:
>>> d = TwoWayDict()
>>> d['foo'] = 'bar'
>>> d['foo']
'bar'
>>> d['bar']
'foo'
>>> len(d)
1
>>> del d['foo']
>>> d['bar']
Traceback (most recent call last):
File "<stdin>", line 7, in <module>
KeyError: 'bar'
I’m sure I didn’t cover all the cases, but that should get you started.
I know it’s an older question, but I wanted to mention another great solution to this problem, namely the python package bidict. It’s extremely straight forward to use:
Two hash maps is actually probably the fastest-performing solution assuming you can spare the memory. I would wrap those in a single class – the burden on the programmer is in ensuring that two the hash maps sync up correctly.
回答 5
您有两个单独的问题。
您有一个“对话”对象。它指的是两个人。由于一个人可以进行多个对话,因此您具有多对多关系。
您有一个从人到会话列表的映射。一个转换将有一对人。
做这样的事情
from collections import defaultdict
switchboard= defaultdict( list )
x =Conversation("Alice","Bob")
y =Conversation("Alice","Charlie")for c in( x, y ):
switchboard[c.p1].append( c )
switchboard[c.p2].append( c )
You have a “Conversation” object. It refers to two Persons. Since a Person can have multiple conversations, you have a many-to-many relationship.
You have a Map from Person to a list of Conversations. A Conversion will have a pair of Persons.
Do something like this
from collections import defaultdict
switchboard= defaultdict( list )
x = Conversation( "Alice", "Bob" )
y = Conversation( "Alice", "Charlie" )
for c in ( x, y ):
switchboard[c.p1].append( c )
switchboard[c.p2].append( c )
No, there is really no way to do this without creating two dictionaries. How would it be possible to implement this with just one dictionary while continuing to offer comparable performance?
You are better off creating a custom type that encapsulates two dictionaries and exposes the functionality you want.
classTwoWayDict(dict):def __init__(self, my_dict):
dict.__init__(self, my_dict)
self.rev_dict ={v : k for k,v in my_dict.iteritems()}def __setitem__(self, key, value):
dict.__setitem__(self, key, value)
self.rev_dict.__setitem__(value, key)def pop(self, key):
self.rev_dict.pop(self[key])
dict.pop(self, key)# The above is just an idea other methods# should also be overridden.
例:
>>> d ={'a':1,'b':2}# suppose we need to use d and its reversed version>>> twd =TwoWayDict(d)# create a two-way dict>>> twd
{'a':1,'b':2}>>> twd.rev_dict
{1:'a',2:'b'}>>> twd['a']1>>> twd.rev_dict[2]'b'>>> twd['c']=3# we add to twd and reversed version also changes>>> twd
{'a':1,'c':3,'b':2}>>> twd.rev_dict
{1:'a',2:'b',3:'c'}>>> twd.pop('a')# we pop elements from twd and reversed version changes>>> twd
{'c':3,'b':2}>>> twd.rev_dict
{2:'b',3:'c'}
Another possible solution is to implement a subclass of dict, that holds the original dictionary and keeps track of a reversed version of it. Keeping two seperate dicts can be useful if keys and values are overlapping.
class TwoWayDict(dict):
def __init__(self, my_dict):
dict.__init__(self, my_dict)
self.rev_dict = {v : k for k,v in my_dict.iteritems()}
def __setitem__(self, key, value):
dict.__setitem__(self, key, value)
self.rev_dict.__setitem__(value, key)
def pop(self, key):
self.rev_dict.pop(self[key])
dict.pop(self, key)
# The above is just an idea other methods
# should also be overridden.
Example:
>>> d = {'a' : 1, 'b' : 2} # suppose we need to use d and its reversed version
>>> twd = TwoWayDict(d) # create a two-way dict
>>> twd
{'a': 1, 'b': 2}
>>> twd.rev_dict
{1: 'a', 2: 'b'}
>>> twd['a']
1
>>> twd.rev_dict[2]
'b'
>>> twd['c'] = 3 # we add to twd and reversed version also changes
>>> twd
{'a': 1, 'c': 3, 'b': 2}
>>> twd.rev_dict
{1: 'a', 2: 'b', 3: 'c'}
>>> twd.pop('a') # we pop elements from twd and reversed version changes
>>> twd
{'c': 3, 'b': 2}
>>> twd.rev_dict
{2: 'b', 3: 'c'}
# This normalization method should save hugely as aDaD ~ yXyX have the same form of smallest grammar.# To get back to your grammar's alphabet use transdef normalize_string(s, nv=None):if nv isNone:
nv = ord('a')
trans = bidict()
r =''for c in s:if c notin trans.inverse:
a = chr(nv)
nv +=1
trans[a]= c
else:
a = trans.inverse[c]
r += a
return r, trans
def translate_string(s, trans):
res =''for c in s:
res += trans[c]return res
if __name__ =="__main__":
s ="bnhnbiodfjos"
n, tr = normalize_string(s)print(n)print(tr)print(translate_string(n, tr))
I like the suggestion of bidict in one of the comments.
pip install bidict
Useage:
# This normalization method should save hugely as aDaD ~ yXyX have the same form of smallest grammar.
# To get back to your grammar's alphabet use trans
def normalize_string(s, nv=None):
if nv is None:
nv = ord('a')
trans = bidict()
r = ''
for c in s:
if c not in trans.inverse:
a = chr(nv)
nv += 1
trans[a] = c
else:
a = trans.inverse[c]
r += a
return r, trans
def translate_string(s, trans):
res = ''
for c in s:
res += trans[c]
return res
if __name__ == "__main__":
s = "bnhnbiodfjos"
n, tr = normalize_string(s)
print(n)
print(tr)
print(translate_string(n, tr))
Since there aren’t much docs about it. But I’ve got all the features I need from it working correctly.
The kjbuckets C extension module provides a “graph” data structure which I believe gives you what you want.
回答 13
这是通过扩展pythons dict类的另一种双向字典实现,以防您不喜欢其他任何字典:
classDoubleD(dict):""" Access and delete dictionary elements by key or value. """def __getitem__(self, key):if key notin self:
inv_dict ={v:k for k,v in self.items()}return inv_dict[key]return dict.__getitem__(self, key)def __delitem__(self, key):if key notin self:
inv_dict ={v:k for k,v in self.items()}
dict.__delitem__(self, inv_dict[key])else:
dict.__delitem__(self, key)
Here’s one more two-way dictionary implementation by extending pythons dict class in case you didn’t like any of those other ones:
class DoubleD(dict):
""" Access and delete dictionary elements by key or value. """
def __getitem__(self, key):
if key not in self:
inv_dict = {v:k for k,v in self.items()}
return inv_dict[key]
return dict.__getitem__(self, key)
def __delitem__(self, key):
if key not in self:
inv_dict = {v:k for k,v in self.items()}
dict.__delitem__(self, inv_dict[key])
else:
dict.__delitem__(self, key)
Use it as a normal python dictionary except in construction: