github.com/songzhibin97/go-baseutils@v0.0.2-0.20240302024150-487d8ce9c082/structure/maps/hashbidimap/hashbidimap.go (about) 1 // Package hashbidimap implements a bidirectional map backed by two hashmaps. 2 // 3 // A bidirectional map, or hash bag, is an associative data structure in which the (key,value) pairs form a one-to-one correspondence. 4 // Thus the binary relation is functional in each direction: value can also act as a key to key. 5 // A pair (a,b) thus provides a unique coupling between 'a' and 'b' so that 'b' can be found when 'a' is used as a key and 'a' can be found when 'b' is used as a key. 6 // 7 // Elements are unordered in the map. 8 // 9 // Structure is not thread safe. 10 // 11 // Reference: https://en.wikipedia.org/wiki/Bidirectional_map 12 package hashbidimap 13 14 import ( 15 "encoding/json" 16 "fmt" 17 18 "github.com/songzhibin97/go-baseutils/structure/maps" 19 "github.com/songzhibin97/go-baseutils/structure/maps/hashmap" 20 ) 21 22 // Assert Map implementation 23 var _ maps.BidiMap[int, int] = (*Map[int, int])(nil) 24 25 // Map holds the elements in two hashmaps. 26 type Map[K comparable, V comparable] struct { 27 forwardMap hashmap.Map[K, V] 28 inverseMap hashmap.Map[V, K] 29 } 30 31 // New instantiates a bidirectional map. 32 func New[K comparable, V comparable]() *Map[K, V] { 33 return &Map[K, V]{*hashmap.New[K, V](), *hashmap.New[V, K]()} 34 } 35 36 // Put inserts element into the map. 37 func (m *Map[K, V]) Put(key K, value V) { 38 if valueByKey, ok := m.forwardMap.Get(key); ok { 39 m.inverseMap.Remove(valueByKey) 40 } 41 if keyByValue, ok := m.inverseMap.Get(value); ok { 42 m.forwardMap.Remove(keyByValue) 43 } 44 m.forwardMap.Put(key, value) 45 m.inverseMap.Put(value, key) 46 } 47 48 // Get searches the element in the map by key and returns its value or nil if key is not found in map. 49 // Second return parameter is true if key was found, otherwise false. 50 func (m *Map[K, V]) Get(key K) (value V, found bool) { 51 return m.forwardMap.Get(key) 52 } 53 54 // GetKey searches the element in the map by value and returns its key or nil if value is not found in map. 55 // Second return parameter is true if value was found, otherwise false. 56 func (m *Map[K, V]) GetKey(value V) (key K, found bool) { 57 return m.inverseMap.Get(value) 58 } 59 60 // Remove removes the element from the map by key. 61 func (m *Map[K, V]) Remove(key K) { 62 if value, found := m.forwardMap.Get(key); found { 63 m.forwardMap.Remove(key) 64 m.inverseMap.Remove(value) 65 } 66 } 67 68 // Empty returns true if map does not contain any elements 69 func (m *Map[K, V]) Empty() bool { 70 return m.Size() == 0 71 } 72 73 // Size returns number of elements in the map. 74 func (m *Map[K, V]) Size() int { 75 return m.forwardMap.Size() 76 } 77 78 // Keys returns all keys (random order). 79 func (m *Map[K, V]) Keys() []K { 80 return m.forwardMap.Keys() 81 } 82 83 // Values returns all values (random order). 84 func (m *Map[K, V]) Values() []V { 85 return m.inverseMap.Keys() 86 } 87 88 // Clear removes all elements from the map. 89 func (m *Map[K, V]) Clear() { 90 m.forwardMap.Clear() 91 m.inverseMap.Clear() 92 } 93 94 // String returns a string representation of container 95 func (m *Map[K, V]) String() string { 96 str := "HashBidiMap\n" 97 str += fmt.Sprintf("%v", m.forwardMap) 98 return str 99 } 100 101 // UnmarshalJSON @implements json.Unmarshaler 102 func (m *Map[K, V]) UnmarshalJSON(bytes []byte) error { 103 elements := make(map[K]V) 104 err := json.Unmarshal(bytes, &elements) 105 if err == nil { 106 m.Clear() 107 for key, value := range elements { 108 m.Put(key, value) 109 } 110 } 111 return err 112 } 113 114 // MarshalJSON @implements json.Marshaler 115 func (m *Map[K, V]) MarshalJSON() ([]byte, error) { 116 return m.forwardMap.MarshalJSON() 117 }