194 lines
5.5 KiB
Go
194 lines
5.5 KiB
Go
// Package orderedmap implements an ordered map, i.e. a map that also keeps track of
|
|
// the order in which keys were inserted.
|
|
//
|
|
// All operations are constant-time.
|
|
//
|
|
// Github repo: https://github.com/wk8/go-ordered-map
|
|
//
|
|
package orderedmap
|
|
|
|
import (
|
|
"container/list"
|
|
"fmt"
|
|
)
|
|
|
|
type Pair struct {
|
|
Key interface{}
|
|
Value interface{}
|
|
|
|
element *list.Element
|
|
}
|
|
|
|
type OrderedMap struct {
|
|
pairs map[interface{}]*Pair
|
|
list *list.List
|
|
}
|
|
|
|
// New creates a new OrderedMap.
|
|
func New() *OrderedMap {
|
|
return &OrderedMap{
|
|
pairs: make(map[interface{}]*Pair),
|
|
list: list.New(),
|
|
}
|
|
}
|
|
|
|
// Get looks for the given key, and returns the value associated with it,
|
|
// or nil if not found. The boolean it returns says whether the key is present in the map.
|
|
func (om *OrderedMap) Get(key interface{}) (interface{}, bool) {
|
|
if pair, present := om.pairs[key]; present {
|
|
return pair.Value, present
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// Load is an alias for Get, mostly to present an API similar to `sync.Map`'s.
|
|
func (om *OrderedMap) Load(key interface{}) (interface{}, bool) {
|
|
return om.Get(key)
|
|
}
|
|
|
|
// GetPair looks for the given key, and returns the pair associated with it,
|
|
// or nil if not found. The Pair struct can then be used to iterate over the ordered map
|
|
// from that point, either forward or backward.
|
|
func (om *OrderedMap) GetPair(key interface{}) *Pair {
|
|
return om.pairs[key]
|
|
}
|
|
|
|
// Set sets the key-value pair, and returns what `Get` would have returned
|
|
// on that key prior to the call to `Set`.
|
|
func (om *OrderedMap) Set(key interface{}, value interface{}) (interface{}, bool) {
|
|
if pair, present := om.pairs[key]; present {
|
|
oldValue := pair.Value
|
|
pair.Value = value
|
|
return oldValue, true
|
|
}
|
|
|
|
pair := &Pair{
|
|
Key: key,
|
|
Value: value,
|
|
}
|
|
pair.element = om.list.PushBack(pair)
|
|
om.pairs[key] = pair
|
|
|
|
return nil, false
|
|
}
|
|
|
|
// Store is an alias for Set, mostly to present an API similar to `sync.Map`'s.
|
|
func (om *OrderedMap) Store(key interface{}, value interface{}) (interface{}, bool) {
|
|
return om.Set(key, value)
|
|
}
|
|
|
|
// Delete removes the key-value pair, and returns what `Get` would have returned
|
|
// on that key prior to the call to `Delete`.
|
|
func (om *OrderedMap) Delete(key interface{}) (interface{}, bool) {
|
|
if pair, present := om.pairs[key]; present {
|
|
om.list.Remove(pair.element)
|
|
delete(om.pairs, key)
|
|
return pair.Value, true
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// Len returns the length of the ordered map.
|
|
func (om *OrderedMap) Len() int {
|
|
return len(om.pairs)
|
|
}
|
|
|
|
// Oldest returns a pointer to the oldest pair. It's meant to be used to iterate on the ordered map's
|
|
// pairs from the oldest to the newest, e.g.:
|
|
// for pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() { fmt.Printf("%v => %v\n", pair.Key, pair.Value) }
|
|
func (om *OrderedMap) Oldest() *Pair {
|
|
return listElementToPair(om.list.Front())
|
|
}
|
|
|
|
// Newest returns a pointer to the newest pair. It's meant to be used to iterate on the ordered map's
|
|
// pairs from the newest to the oldest, e.g.:
|
|
// for pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() { fmt.Printf("%v => %v\n", pair.Key, pair.Value) }
|
|
func (om *OrderedMap) Newest() *Pair {
|
|
return listElementToPair(om.list.Back())
|
|
}
|
|
|
|
// Next returns a pointer to the next pair.
|
|
func (p *Pair) Next() *Pair {
|
|
return listElementToPair(p.element.Next())
|
|
}
|
|
|
|
// Previous returns a pointer to the previous pair.
|
|
func (p *Pair) Prev() *Pair {
|
|
return listElementToPair(p.element.Prev())
|
|
}
|
|
|
|
func listElementToPair(element *list.Element) *Pair {
|
|
if element == nil {
|
|
return nil
|
|
}
|
|
return element.Value.(*Pair)
|
|
}
|
|
|
|
// KeyNotFoundError may be returned by functions in this package when they're called with keys that are not present
|
|
// in the map.
|
|
type KeyNotFoundError struct {
|
|
MissingKey interface{}
|
|
}
|
|
|
|
var _ error = &KeyNotFoundError{}
|
|
|
|
func (e *KeyNotFoundError) Error() string {
|
|
return fmt.Sprintf("missing key: %v", e.MissingKey)
|
|
}
|
|
|
|
// MoveAfter moves the value associated with key to its new position after the one associated with markKey.
|
|
// Returns an error iff key or markKey are not present in the map.
|
|
func (om *OrderedMap) MoveAfter(key, markKey interface{}) error {
|
|
elements, err := om.getElements(key, markKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
om.list.MoveAfter(elements[0], elements[1])
|
|
return nil
|
|
}
|
|
|
|
// MoveBefore moves the value associated with key to its new position before the one associated with markKey.
|
|
// Returns an error iff key or markKey are not present in the map.
|
|
func (om *OrderedMap) MoveBefore(key, markKey interface{}) error {
|
|
elements, err := om.getElements(key, markKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
om.list.MoveBefore(elements[0], elements[1])
|
|
return nil
|
|
}
|
|
|
|
func (om *OrderedMap) getElements(keys ...interface{}) ([]*list.Element, error) {
|
|
elements := make([]*list.Element, len(keys))
|
|
for i, k := range keys {
|
|
pair, present := om.pairs[k]
|
|
if !present {
|
|
return nil, &KeyNotFoundError{k}
|
|
}
|
|
elements[i] = pair.element
|
|
}
|
|
return elements, nil
|
|
}
|
|
|
|
// MoveToBack moves the value associated with key to the back of the ordered map.
|
|
// Returns an error iff key is not present in the map.
|
|
func (om *OrderedMap) MoveToBack(key interface{}) error {
|
|
pair, present := om.pairs[key]
|
|
if !present {
|
|
return &KeyNotFoundError{key}
|
|
}
|
|
om.list.MoveToBack(pair.element)
|
|
return nil
|
|
}
|
|
|
|
// MoveToFront moves the value associated with key to the front of the ordered map.
|
|
// Returns an error iff key is not present in the map.
|
|
func (om *OrderedMap) MoveToFront(key interface{}) error {
|
|
pair, present := om.pairs[key]
|
|
if !present {
|
|
return &KeyNotFoundError{key}
|
|
}
|
|
om.list.MoveToFront(pair.element)
|
|
return nil
|
|
}
|