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  }