Skip to content

Commit

Permalink
Adding GetAndMoveToBack() and GetAndMoveToFront() methods
Browse files Browse the repository at this point in the history
Makes it easier to use ordered maps to implement e.g. LRUs.
  • Loading branch information
wk8 committed Feb 15, 2023
1 parent 02b9222 commit f4d64d4
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 18 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

[comment]: # (Changes since last release go here)

## 2.1.6 - Feb 15th 2023

* Added `GetAndMoveToBack()` and `GetAndMoveToFront()` methods

## 2.1.5 - Dec 13th 2022

* Added `Value()` method
Expand Down
59 changes: 42 additions & 17 deletions orderedmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ func (om *OrderedMap[K, V]) GetPair(key K) *Pair[K, V] {

// 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[K, V]) Set(key K, value V) (val V, present bool) {
if pair, present := om.pairs[key]; present {
oldValue := pair.Value
Expand Down Expand Up @@ -216,7 +217,8 @@ func (e *KeyNotFoundError[K]) Error() string {
}

// 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.
// Returns an error iff key or markKey are not present in the map. If an error is returned,
// it will be a KeyNotFoundError.
func (om *OrderedMap[K, V]) MoveAfter(key, markKey K) error {
elements, err := om.getElements(key, markKey)
if err != nil {
Expand All @@ -227,7 +229,8 @@ func (om *OrderedMap[K, V]) MoveAfter(key, markKey K) error {
}

// 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.
// Returns an error iff key or markKey are not present in the map. If an error is returned,
// it will be a KeyNotFoundError.
func (om *OrderedMap[K, V]) MoveBefore(key, markKey K) error {
elements, err := om.getElements(key, markKey)
if err != nil {
Expand All @@ -249,24 +252,46 @@ func (om *OrderedMap[K, V]) getElements(keys ...K) ([]*list.Element[*Pair[K, V]]
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.
// MoveToBack moves the value associated with key to the back of the ordered map,
// i.e. makes it the newest pair in the map.
// Returns an error iff key is not present in the map. If an error is returned,
// it will be a KeyNotFoundError.
func (om *OrderedMap[K, V]) MoveToBack(key K) error {
pair, present := om.pairs[key]
if !present {
return &KeyNotFoundError[K]{key}
}
om.list.MoveToBack(pair.element)
return nil
_, err := om.GetAndMoveToBack(key)
return err
}

// 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.
// MoveToFront moves the value associated with key to the front of the ordered map,
// i.e. makes it the oldest pair in the map.
// Returns an error iff key is not present in the map. If an error is returned,
// it will be a KeyNotFoundError.
func (om *OrderedMap[K, V]) MoveToFront(key K) error {
pair, present := om.pairs[key]
if !present {
return &KeyNotFoundError[K]{key}
_, err := om.GetAndMoveToFront(key)
return err
}

// GetAndMoveToBack combines Get and MoveToBack in the same call. If an error is returned,
// it will be a KeyNotFoundError.
func (om *OrderedMap[K, V]) GetAndMoveToBack(key K) (val V, err error) {
if pair, present := om.pairs[key]; present {
val = pair.Value
om.list.MoveToBack(pair.element)
} else {
err = &KeyNotFoundError[K]{key}
}
om.list.MoveToFront(pair.element)
return nil

return
}

// GetAndMoveToFront combines Get and MoveToFront in the same call. If an error is returned,
// it will be a KeyNotFoundError.
func (om *OrderedMap[K, V]) GetAndMoveToFront(key K) (val V, err error) {
if pair, present := om.pairs[key]; present {
val = pair.Value
om.list.MoveToFront(pair.element)
} else {
err = &KeyNotFoundError[K]{key}
}

return
}
31 changes: 30 additions & 1 deletion orderedmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,36 @@ func TestMove(t *testing.T) {
[]any{"28", "bar", 28, "100", "baz", "baz", "baz", 100})

err = om.MoveToFront(100)
assert.NotEqual(t, err, nil)
assert.Equal(t, &KeyNotFoundError[int]{100}, err)
}

func TestGetAndMove(t *testing.T) {
om := New[int, any]()
om.Set(1, "bar")
om.Set(2, 28)
om.Set(3, 100)
om.Set(4, "baz")
om.Set(5, "28")
om.Set(6, "100")
om.Set(7, "baz")
om.Set(8, "baz")

value, err := om.GetAndMoveToBack(3)
assert.Nil(t, err)
assert.Equal(t, 100, value)
assertOrderedPairsEqual(t, om,
[]int{1, 2, 4, 5, 6, 7, 8, 3},
[]any{"bar", 28, "baz", "28", "100", "baz", "baz", 100})

value, err = om.GetAndMoveToFront(5)
assert.Nil(t, err)
assert.Equal(t, "28", value)
assertOrderedPairsEqual(t, om,
[]int{5, 1, 2, 4, 6, 7, 8, 3},
[]any{"28", "bar", 28, "baz", "100", "baz", "baz", 100})

value, err = om.GetAndMoveToBack(100)
assert.Equal(t, &KeyNotFoundError[int]{100}, err)
}

func TestAddPairs(t *testing.T) {
Expand Down
2 changes: 2 additions & 0 deletions test_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
)

// assertOrderedPairsEqual asserts that the map contains the given keys and values
// from oldest to newest.
func assertOrderedPairsEqual[K comparable, V any](
t *testing.T, orderedMap *OrderedMap[K, V], expectedKeys []K, expectedValues []V,
) {
Expand Down

0 comments on commit f4d64d4

Please sign in to comment.