blob: bbcbeddbd9d3513a2ff8268ba1b70cf9a9924fb8 [file] [log] [blame]
package mapsets
import (
"sort"
"golang.org/x/exp/constraints"
)
// OrderedMap is a map from K to V which provides total ordering as defined by
// the keys used.
//
// The K type used must implement total ordering and comparability per the Key
// interface, and must not be mutated after being inserted (or at least, must
// keep the same ordering expressed as part of its Key implementation). A string
// (or string wrapping type) is a good key, as strings in Go are immutable and
// act like pointers.
//
// The values used will be copied by the map implementation. For structured
// types, defining V to be a pointer to a structure is probably the correct
// choice.
//
// An empty OrderedMap is ready to use and will contain no elements.
//
// It is not safe to be used by multiple goroutines. It can be locked behind a
// sync.RWMutex, with a read lock taken for any method that does not mutate the
// map, and with a write lock taken for any method that does mutate it.
type OrderedMap[K Key, V any] struct {
keys []K
values map[K]V
}
// Key must be implemented by keys used by OrderedMap. Most 'typical' key types
// (string, integers, etc.) already implement it.
type Key interface {
comparable
constraints.Ordered
}
func (s *OrderedMap[K, V]) sort() {
sort.Slice(s.keys, func(i, j int) bool {
return s.keys[i] < s.keys[j]
})
}
// Insert a given value at a given key. If a value at this key already exists, it
// will be overwritten with the new value.
//
// This method mutates the map.
func (s *OrderedMap[K, V]) Insert(k K, v V) {
if s.values == nil {
s.values = make(map[K]V)
}
if _, ok := s.values[k]; !ok {
s.keys = append(s.keys, k)
s.sort()
}
s.values[k] = v
}
// Get a value at a given key. If there is no value for the given key, an empty V
// and false will be returned.
//
// This returns a copy of the stored value.
func (s *OrderedMap[K, V]) Get(k K) (V, bool) {
var zero V
if s.values == nil {
return zero, false
}
v, ok := s.values[k]
return v, ok
}
// Keys returns a copy of the keys of this map, ordered according to the Key
// implementation used.
func (s *OrderedMap[K, V]) Keys() []K {
if s.values == nil {
return nil
}
keys := make([]K, len(s.keys))
for i, k := range s.keys {
keys[i] = k
}
return keys
}
// Values returns a copy of all the keys and values of this map, ordered
// according to the key implementation used.
func (s *OrderedMap[K, V]) Values() []KeyValue[K, V] {
var res []KeyValue[K, V]
for _, k := range s.keys {
res = append(res, KeyValue[K, V]{
Key: k,
Value: s.values[k],
})
}
return res
}
// KeyValue represents a value V at a key K of an OrderedMap.
type KeyValue[K Key, V any] struct {
Key K
Value V
}
// Delete the value at the given key, if present.
//
// This method mutates the map.
func (s *OrderedMap[K, V]) Delete(k K) {
// Short-circuit delete in empty map.
if s.values == nil {
return
}
// Key not set? Just return.
if _, ok := s.values[k]; !ok {
return
}
// Iterate over keys to find index.
toRemove := -1
for i, k2 := range s.keys {
if k2 == k {
toRemove = i
break
}
}
if toRemove == -1 {
panic("programming error: keys and values out of sync in OrderedMap")
}
// No need to re-sort, as the keys were already sorted.
left := s.keys[0:toRemove]
right := s.keys[toRemove+1:]
s.keys = append(left, right...)
delete(s.values, k)
}
// Clone (perform a deep copy) of the OrderedMap, copying all keys and values.
func (s *OrderedMap[K, V]) Clone() OrderedMap[K, V] {
// Short-circuit clone of empty map.
if s.values == nil {
return OrderedMap[K, V]{}
}
keys := make([]K, len(s.keys))
values := make(map[K]V)
for i, k := range s.keys {
keys[i] = k
values[k] = s.values[k]
}
return OrderedMap[K, V]{
keys: keys,
values: values,
}
}
// Replace all contents of this map with the contents of another map. The other
// map must not be concurrently accessed.
//
// This method mutates the map.
func (s *OrderedMap[K, V]) Replace(o *OrderedMap[K, V]) {
if s.values == nil {
s.values = make(map[K]V)
}
cloned := o.Clone()
s.keys = cloned.keys
s.values = cloned.values
}
// Cycle returns a CycleIterator referencing this OrderedMap.
//
// This iterator will cycle through all the keys/values of the OrderedMap, then
// wrap around again when it reaches the end of the OrderedMap. This behaviour is
// designed to be used in round-robin mechanisms.
//
// As the OrderedMap changes, the returned CycleIterator will also change:
// - If a key is removed or added, the CycleIterator is guaranteed to return it
// within one entire cycle through all the values.
// - If a value is changed, the new value is guaranteed to be returned on the
// next iteration when the previous value would have been returned.
//
// Generally, it is safe to hold on to OrderedMap pointers and mutate the
// underlying OrderedMap.
//
// However, concurrent access to the OrderedMap and CycleIterator from different
// goroutines is not safe. Access to the CycleIterator should be guarded behind
// the same mutual exclusion mechanism as access to the OrderedMap.
func (s *OrderedMap[K, V]) Cycle() CycleIterator[K, V] {
return CycleIterator[K, V]{
set: s,
}
}
// CycleIterator for an OrderedMap. See OrderedMap.Cycle for more details.
type CycleIterator[K Key, V any] struct {
set *OrderedMap[K, V]
ix int
}
// Reset the iteration to the beginning of the ordered map.
func (c *CycleIterator[K, V]) Reset() {
c.ix = 0
}
// Next returns the next key and value of the OrderedMap and a bool describing
// the iteration state. If the OrderedMap is empty (and thus can't provide any
// keys/values, including for this iteration), false will be returned in the
// third value.
func (c *CycleIterator[K, V]) Next() (K, V, bool) {
var zeroK K
var zeroV V
if len(c.set.keys) == 0 {
return zeroK, zeroV, false
}
if c.ix >= len(c.set.keys) {
c.ix = 0
}
key := c.set.keys[c.ix]
val := c.set.values[key]
c.ix++
return key, val, true
}