github.com/songzhibin97/go-baseutils@v0.0.2-0.20240302024150-487d8ce9c082/structure/maps/linkedhashmap/linkedhashmap.go (about)

     1  // Package linkedhashmap is a map that preserves insertion-order.
     2  //
     3  // It is backed by a hash table to store values and doubly-linked list to store ordering.
     4  //
     5  // Structure is not thread safe.
     6  //
     7  // Reference: http://en.wikipedia.org/wiki/Associative_array
     8  package linkedhashmap
     9  
    10  import (
    11  	"bytes"
    12  	"encoding/json"
    13  	"fmt"
    14  	"strings"
    15  
    16  	"github.com/songzhibin97/go-baseutils/base/bcomparator"
    17  	"github.com/songzhibin97/go-baseutils/structure/lists/doublylinkedlist"
    18  	"github.com/songzhibin97/go-baseutils/structure/maps"
    19  )
    20  
    21  // Assert Map implementation
    22  var _ maps.Map[int, any] = (*Map[int, any])(nil)
    23  
    24  // Map holds the elements in a regular hash table, and uses doubly-linked list to store key ordering.
    25  type Map[K comparable, V any] struct {
    26  	table    map[K]V
    27  	ordering *doublylinkedlist.List[K]
    28  	zeroK    K
    29  	zeroV    V
    30  }
    31  
    32  // New instantiates a linked-hash-map.
    33  func New[K comparable, V any]() *Map[K, V] {
    34  	return &Map[K, V]{
    35  		table:    make(map[K]V),
    36  		ordering: doublylinkedlist.New[K](),
    37  	}
    38  }
    39  
    40  // Put inserts key-value pair into the map.
    41  // Key should adhere to the comparator's type assertion, otherwise method panics.
    42  func (m *Map[K, V]) Put(key K, value V) {
    43  	if _, contains := m.table[key]; !contains {
    44  		m.ordering.Append(key)
    45  	}
    46  	m.table[key] = value
    47  }
    48  
    49  // Get searches the element in the map by key and returns its value or nil if key is not found in tree.
    50  // Second return parameter is true if key was found, otherwise false.
    51  // Key should adhere to the comparator's type assertion, otherwise method panics.
    52  func (m *Map[K, V]) Get(key K) (value V, found bool) {
    53  	value, found = m.table[key]
    54  	return
    55  }
    56  
    57  // Remove removes the element from the map by key.
    58  // Key should adhere to the comparator's type assertion, otherwise method panics.
    59  func (m *Map[K, V]) Remove(key K) {
    60  	if _, contains := m.table[key]; contains {
    61  		delete(m.table, key)
    62  		index := m.ordering.IndexOf(key)
    63  		m.ordering.Remove(index)
    64  	}
    65  }
    66  
    67  // Empty returns true if map does not contain any elements
    68  func (m *Map[K, V]) Empty() bool {
    69  	return m.Size() == 0
    70  }
    71  
    72  // Size returns number of elements in the map.
    73  func (m *Map[K, V]) Size() int {
    74  	return m.ordering.Size()
    75  }
    76  
    77  // Keys returns all keys in-order
    78  func (m *Map[K, V]) Keys() []K {
    79  	return m.ordering.Values()
    80  }
    81  
    82  // Values returns all values in-order based on the key.
    83  func (m *Map[K, V]) Values() []V {
    84  	values := make([]V, m.Size())
    85  	count := 0
    86  	it := m.Iterator()
    87  	for it.Next() {
    88  		values[count] = it.Value()
    89  		count++
    90  	}
    91  	return values
    92  }
    93  
    94  // Clear removes all elements from the map.
    95  func (m *Map[K, V]) Clear() {
    96  	m.table = make(map[K]V)
    97  	m.ordering.Clear()
    98  }
    99  
   100  // String returns a string representation of container
   101  func (m *Map[K, V]) String() string {
   102  	bf := strings.Builder{}
   103  	bf.WriteString("LinkedHashMap\nmap[")
   104  	it := m.Iterator()
   105  	for it.Next() {
   106  		bf.WriteString(fmt.Sprintf("(%v:%v) ", it.Key(), it.Value()))
   107  	}
   108  	bf.WriteString("]")
   109  	return bf.String()
   110  }
   111  
   112  // UnmarshalJSON @implements json.Unmarshaler
   113  func (m *Map[K, V]) UnmarshalJSON(data []byte) error {
   114  	elements := make(map[K]V)
   115  	err := json.Unmarshal(data, &elements)
   116  	if err != nil {
   117  		return err
   118  	}
   119  
   120  	index := make(map[K]int)
   121  	var keys []K
   122  	for key := range elements {
   123  		keys = append(keys, key)
   124  		esc, _ := json.Marshal(key)
   125  		index[key] = bytes.Index(data, esc)
   126  	}
   127  
   128  	byIndex := func(a, b K) int {
   129  		key1 := a
   130  		key2 := b
   131  		index1 := index[key1]
   132  		index2 := index[key2]
   133  		return index1 - index2
   134  	}
   135  
   136  	bcomparator.Sort(keys, byIndex)
   137  
   138  	m.Clear()
   139  
   140  	for _, key := range keys {
   141  		m.Put(key, elements[key])
   142  	}
   143  
   144  	return nil
   145  }
   146  
   147  // MarshalJSON @implements json.Marshaler
   148  func (m *Map[K, V]) MarshalJSON() ([]byte, error) {
   149  	var b []byte
   150  	buf := bytes.NewBuffer(b)
   151  
   152  	buf.WriteRune('{')
   153  
   154  	it := m.Iterator()
   155  	lastIndex := m.Size() - 1
   156  	index := 0
   157  
   158  	for it.Next() {
   159  		km, err := json.Marshal(it.Key())
   160  		if err != nil {
   161  			return nil, err
   162  		}
   163  		buf.Write(km)
   164  
   165  		buf.WriteRune(':')
   166  
   167  		vm, err := json.Marshal(it.Value())
   168  		if err != nil {
   169  			return nil, err
   170  		}
   171  		buf.Write(vm)
   172  
   173  		if index != lastIndex {
   174  			buf.WriteRune(',')
   175  		}
   176  
   177  		index++
   178  	}
   179  
   180  	buf.WriteRune('}')
   181  
   182  	return buf.Bytes(), nil
   183  }