-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Generics (in Go 1.18) are an ideal case for this library. It is provided through a new version v2. If you need to support Go 1.17 or below you can continue to use v1.
- Loading branch information
1 parent
1bd6a21
commit da74303
Showing
7 changed files
with
547 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package orderedmap | ||
|
||
import ( | ||
"container/list" | ||
"golang.org/x/exp/constraints" | ||
) | ||
|
||
type Element[K constraints.Ordered, V any] struct { | ||
Key K | ||
Value V | ||
|
||
element *list.Element | ||
} | ||
|
||
func newElement[K constraints.Ordered, V any](e *list.Element) *Element[K, V] { | ||
if e == nil { | ||
return nil | ||
} | ||
|
||
element := e.Value.(*orderedMapElement[K, V]) | ||
|
||
return &Element[K, V]{ | ||
element: e, | ||
Key: element.key, | ||
Value: element.value, | ||
} | ||
} | ||
|
||
// Next returns the next element, or nil if it finished. | ||
func (e *Element[K, V]) Next() *Element[K, V] { | ||
return newElement[K, V](e.element.Next()) | ||
} | ||
|
||
// Prev returns the previous element, or nil if it finished. | ||
func (e *Element[K, V]) Prev() *Element[K, V] { | ||
return newElement[K, V](e.element.Prev()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package orderedmap_test | ||
|
||
import ( | ||
"github.com/elliotchance/orderedmap/v2" | ||
"github.com/stretchr/testify/assert" | ||
"testing" | ||
) | ||
|
||
func TestElement_Key(t *testing.T) { | ||
t.Run("Front", func(t *testing.T) { | ||
m := orderedmap.NewOrderedMap[int, string]() | ||
m.Set(1, "foo") | ||
m.Set(2, "bar") | ||
assert.Equal(t, 1, m.Front().Key) | ||
}) | ||
|
||
t.Run("Back", func(t *testing.T) { | ||
m := orderedmap.NewOrderedMap[int, string]() | ||
m.Set(1, "foo") | ||
m.Set(2, "bar") | ||
assert.Equal(t, 2, m.Back().Key) | ||
}) | ||
} | ||
|
||
func TestElement_Value(t *testing.T) { | ||
t.Run("Front", func(t *testing.T) { | ||
m := orderedmap.NewOrderedMap[int, string]() | ||
m.Set(1, "foo") | ||
m.Set(2, "bar") | ||
assert.Equal(t, "foo", m.Front().Value) | ||
}) | ||
|
||
t.Run("Back", func(t *testing.T) { | ||
m := orderedmap.NewOrderedMap[int, string]() | ||
m.Set(1, "foo") | ||
m.Set(2, "bar") | ||
assert.Equal(t, "bar", m.Back().Value) | ||
}) | ||
} | ||
|
||
func TestElement_Next(t *testing.T) { | ||
m := orderedmap.NewOrderedMap[int, string]() | ||
m.Set(1, "foo") | ||
m.Set(2, "bar") | ||
m.Set(3, "baz") | ||
|
||
var results []any | ||
for el := m.Front(); el != nil; el = el.Next() { | ||
results = append(results, el.Key, el.Value) | ||
} | ||
|
||
assert.Equal(t, []any{1, "foo", 2, "bar", 3, "baz"}, results) | ||
} | ||
|
||
func TestElement_Prev(t *testing.T) { | ||
m := orderedmap.NewOrderedMap[int, string]() | ||
m.Set(1, "foo") | ||
m.Set(2, "bar") | ||
m.Set(3, "baz") | ||
|
||
var results []any | ||
for el := m.Back(); el != nil; el = el.Prev() { | ||
results = append(results, el.Key, el.Value) | ||
} | ||
|
||
assert.Equal(t, []any{3, "baz", 2, "bar", 1, "foo"}, results) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
module github.com/elliotchance/orderedmap/v2 | ||
|
||
go 1.18 | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.0 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
github.com/stretchr/testify v1.7.1 // indirect | ||
golang.org/x/exp v0.0.0-20220321173239-a90fa8a75705 // indirect | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= | ||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
golang.org/x/exp v0.0.0-20220321173239-a90fa8a75705 h1:ba9YlqfDGTTQ5aZ2fwOoQ1hf32QySyQkR6ODGDzHlnE= | ||
golang.org/x/exp v0.0.0-20220321173239-a90fa8a75705/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
package orderedmap | ||
|
||
import ( | ||
"container/list" | ||
"golang.org/x/exp/constraints" | ||
) | ||
|
||
type orderedMapElement[K constraints.Ordered, V any] struct { | ||
key K | ||
value V | ||
} | ||
|
||
type OrderedMap[K constraints.Ordered, V any] struct { | ||
kv map[K]*list.Element | ||
ll *list.List | ||
} | ||
|
||
func NewOrderedMap[K constraints.Ordered, V any]() *OrderedMap[K, V] { | ||
return &OrderedMap[K, V]{ | ||
kv: make(map[K]*list.Element), | ||
ll: list.New(), | ||
} | ||
} | ||
|
||
// Get returns the value for a key. If the key does not exist, the second return | ||
// parameter will be false and the value will be nil. | ||
func (m *OrderedMap[K, V]) Get(key K) (value V, ok bool) { | ||
v, ok := m.kv[key] | ||
if ok { | ||
value = v.Value.(*orderedMapElement[K, V]).value | ||
} | ||
|
||
return | ||
} | ||
|
||
// Set will set (or replace) a value for a key. If the key was new, then true | ||
// will be returned. The returned value will be false if the value was replaced | ||
// (even if the value was the same). | ||
func (m *OrderedMap[K, V]) Set(key K, value V) bool { | ||
_, didExist := m.kv[key] | ||
|
||
if !didExist { | ||
element := m.ll.PushBack(&orderedMapElement[K, V]{key, value}) | ||
m.kv[key] = element | ||
} else { | ||
m.kv[key].Value.(*orderedMapElement[K, V]).value = value | ||
} | ||
|
||
return !didExist | ||
} | ||
|
||
// GetOrDefault returns the value for a key. If the key does not exist, returns | ||
// the default value instead. | ||
func (m *OrderedMap[K, V]) GetOrDefault(key K, defaultValue V) V { | ||
if value, ok := m.kv[key]; ok { | ||
return value.Value.(*orderedMapElement[K, V]).value | ||
} | ||
|
||
return defaultValue | ||
} | ||
|
||
// GetElement returns the element for a key. If the key does not exist, the | ||
// pointer will be nil. | ||
func (m *OrderedMap[K, V]) GetElement(key K) *Element[K, V] { | ||
value, ok := m.kv[key] | ||
if ok { | ||
element := value.Value.(*orderedMapElement[K, V]) | ||
return &Element[K, V]{ | ||
element: value, | ||
Key: element.key, | ||
Value: element.value, | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Len returns the number of elements in the map. | ||
func (m *OrderedMap[K, V]) Len() int { | ||
return len(m.kv) | ||
} | ||
|
||
// Keys returns all of the keys in the order they were inserted. If a key was | ||
// replaced it will retain the same position. To ensure most recently set keys | ||
// are always at the end you must always Delete before Set. | ||
func (m *OrderedMap[K, V]) Keys() (keys []K) { | ||
keys = make([]K, m.Len()) | ||
|
||
element := m.ll.Front() | ||
for i := 0; element != nil; i++ { | ||
keys[i] = element.Value.(*orderedMapElement[K, V]).key | ||
element = element.Next() | ||
} | ||
|
||
return keys | ||
} | ||
|
||
// Delete will remove a key from the map. It will return true if the key was | ||
// removed (the key did exist). | ||
func (m *OrderedMap[K, V]) Delete(key K) (didDelete bool) { | ||
element, ok := m.kv[key] | ||
if ok { | ||
m.ll.Remove(element) | ||
delete(m.kv, key) | ||
} | ||
|
||
return ok | ||
} | ||
|
||
// Front will return the element that is the first (oldest Set element). If | ||
// there are no elements this will return nil. | ||
func (m *OrderedMap[K, V]) Front() *Element[K, V] { | ||
front := m.ll.Front() | ||
if front == nil { | ||
return nil | ||
} | ||
|
||
element := front.Value.(*orderedMapElement[K, V]) | ||
|
||
return &Element[K, V]{ | ||
element: front, | ||
Key: element.key, | ||
Value: element.value, | ||
} | ||
} | ||
|
||
// Back will return the element that is the last (most recent Set element). If | ||
// there are no elements this will return nil. | ||
func (m *OrderedMap[K, V]) Back() *Element[K, V] { | ||
back := m.ll.Back() | ||
if back == nil { | ||
return nil | ||
} | ||
|
||
element := back.Value.(*orderedMapElement[K, V]) | ||
|
||
return &Element[K, V]{ | ||
element: back, | ||
Key: element.key, | ||
Value: element.value, | ||
} | ||
} | ||
|
||
// Copy returns a new OrderedMap with the same elements. | ||
// Using Copy while there are concurrent writes may mangle the result. | ||
func (m *OrderedMap[K, V]) Copy() *OrderedMap[K, V] { | ||
m2 := NewOrderedMap[K, V]() | ||
|
||
for el := m.Front(); el != nil; el = el.Next() { | ||
m2.Set(el.Key, el.Value) | ||
} | ||
|
||
return m2 | ||
} |
Oops, something went wrong.