Двухсторонняя / обратная карта - PullRequest
81 голосов
/ 21 сентября 2009

Я делаю это переключение в Python, где мне нужно отслеживать, кто с кем разговаривает, так что если Алиса -> Боб, то это подразумевает, что Боб -> Алиса.

Да, я мог бы заполнить две хеш-карты, но мне интересно, есть ли у кого-нибудь идея сделать это с одной.

Или предложить другую структуру данных.

Нет нескольких разговоров. Допустим, это для колл-центра обслуживания клиентов, поэтому, когда Алиса набирает номер на коммутаторе, она только собирается поговорить с Бобом. Его ответы также относятся только к ней.

Ответы [ 13 ]

79 голосов
/ 07 ноября 2012

Вы можете создать свой собственный тип словаря, создав подкласс dict и добавив нужную логику. Вот базовый пример:

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

И это работает так:

>>> 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'

Я уверен, что не охватил все случаи, но это должно помочь вам начать.

39 голосов
/ 21 сентября 2009

В вашем особом случае вы можете хранить оба в одном словаре:

relation = {}
relation['Alice'] = 'Bob'
relation['Bob'] = 'Alice'

Поскольку вы описываете симметричные отношения. A -> B => B -> A

21 голосов
/ 21 сентября 2009

Я бы просто заполнил второй хеш с

reverse_map = dict((reversed(item) for item in forward_map.items()))
14 голосов
/ 05 февраля 2018

Я знаю, что это более старый вопрос, но я хотел упомянуть еще одно замечательное решение этой проблемы, а именно пакет python bidict . Это чрезвычайно просто использовать:

from bidict import bidict
map = bidict(Bob = "Alice")
print(map["Bob"])
print(map.inv["Alice"])
8 голосов
/ 21 сентября 2009

Две хеш-карты - это, вероятно, самое быстрое решение, при условии, что вы можете сэкономить память. Я бы обернул их в один класс - бремя для программиста заключается в обеспечении правильной синхронизации двух хэш-карт.

5 голосов
/ 21 сентября 2009

У вас есть две отдельные проблемы.

  1. У вас есть объект "Разговор". Это относится к двум лицам. Поскольку у человека может быть несколько разговоров, у вас есть отношения многие ко многим.

  2. У вас есть карта от человека до списка разговоров. Конверсия будет иметь пару человек.

Сделай что-нибудь подобное

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 )
4 голосов
/ 21 сентября 2009

Нет, на самом деле нет способа сделать это без создания двух словарей. Как можно было бы реализовать это с помощью всего одного словаря, продолжая предлагать сопоставимую производительность?

Вам лучше создать пользовательский тип, который инкапсулирует два словаря и предоставляет нужные вам функции.

1 голос
/ 15 февраля 2019

Мне нравится предложение о бидике в одном из комментариев.

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

Поскольку не так много документов по этому поводу. Но у меня все функции, которые мне нужны, работают правильно.

Печать:

abcbadefghei
bidict({'a': 'b', 'b': 'n', 'c': 'h', 'd': 'i', 'e': 'o', 'f': 'd', 'g': 'f', 'h': 'j', 'i': 's'})
bnhnbiodfjos
1 голос
/ 04 декабря 2015

На pypi есть библиотека с расширенными коллекциями: https://pypi.python.org/pypi/collections-extended/0.6.0

Использование класса bijection так же просто, как:

RESPONSE_TYPES = bijection({
    0x03 : 'module_info',
    0x09 : 'network_status_response',
    0x10 : 'trust_center_device_update'
})
>>> RESPONSE_TYPES[0x03]
'module_info'
>>> RESPONSE_TYPES.inverse['network_status_response']
0x09
1 голос
/ 04 августа 2013

Другое возможное решение - реализовать подкласс dict, который содержит исходный словарь и отслеживает его обратную версию. Хранение двух отдельных кодов может быть полезным, если ключи и значения перекрываются.

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. 

Пример:

>>> 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'}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...