Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions examples/examples_generics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//go:build go1.18
// +build go1.18

package main

import (
"fmt"

"github.com/cevaris/ordered_map"
)

func GetAndSetExampleG() {
// Init new OrderedMap
om := ordered_map.NewOrderedMapG[string, int]()

// Set key
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Set("d", 4)

// Same interface as builtin map
if val, ok := om.Get("b"); ok {
// Found key "b"
fmt.Println(val)
}

// Delete a key
om.Delete("c")

// Failed Get lookup becase we deleted "c"
if _, ok := om.Get("c"); !ok {
// Did not find key "c"
fmt.Println("c not found")
}
}

func IteratorExampleG() {
n := 100
om := ordered_map.NewOrderedMapG[int, string]()

for i := 0; i < n; i++ {
// Insert data into OrderedMap
om.Set(i, fmt.Sprintf("%d", i*i))
}

// Iterate though values
// - Values iteration are in insert order
// - Returned in a key/value pair struct
iter := om.IterFunc()
for kv, ok := iter(); ok; kv, ok = iter() {
fmt.Println(kv, kv.Key, kv.Value)
}
}

type MyStructG struct {
a int
b float64
}

func CustomStructG() {
om := ordered_map.NewOrderedMapG[string, *MyStructG]()
om.Set("one", &MyStructG{1, 1.1})
om.Set("two", &MyStructG{2, 2.2})
om.Set("three", &MyStructG{3, 3.3})

fmt.Println(om)
// Ouput: OrderedMap[one:&{1 1.1}, two:&{2 2.2}, three:&{3 3.3}]

}

// func main() {
// GetAndSetExampleG()
// IteratorExampleG()
// CustomStructG()
// }
68 changes: 68 additions & 0 deletions generics_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//go:build go1.18
// +build go1.18

package ordered_map

import (
"testing"
)

func BenchmarkSet_OrderedMap(b *testing.B) {
om := NewOrderedMap()
b.ResetTimer()
for i := 0; i < b.N; i++ {
om.Set(i, i)
}
}

func BenchmarkSet_OrderedMapG(b *testing.B) {
om := NewOrderedMapG[int, int]()
b.ResetTimer()
for i := 0; i < b.N; i++ {
om.Set(i, i)
}
}

func BenchmarkGet_OrderedMap(b *testing.B) {
om := NewOrderedMap()
for i := 0; i < b.N; i++ {
om.Set(i, i)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
om.Get(i)
}
}

func BenchmarkGet_OrderedMapG(b *testing.B) {
om := NewOrderedMapG[int, int]()
for i := 0; i < b.N; i++ {
om.Set(i, i)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
om.Get(i)
}
}

func BenchmarkDelete_OrderedMap(b *testing.B) {
om := NewOrderedMap()
for i := 0; i < b.N; i++ {
om.Set(i, i)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
om.Delete(i)
}
}

func BenchmarkDelete_OrderedMapG(b *testing.B) {
om := NewOrderedMapG[int, int]()
for i := 0; i < b.N; i++ {
om.Set(i, i)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
om.Delete(i)
}
}
15 changes: 15 additions & 0 deletions key_pair_generics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//go:build go1.18
// +build go1.18

package ordered_map

import "fmt"

type KVPairG[K comparable, V any] struct {
Key K
Value V
}

func (k *KVPairG[K, V]) String() string {
return fmt.Sprintf("%v:%v", k.Key, k.Value)
}
26 changes: 26 additions & 0 deletions node_generics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//go:build go1.18
// +build go1.18

package ordered_map

type nodeG[K comparable, V any] struct {
prev *nodeG[K, V]
next *nodeG[K, V]
key K
value V
}

func newRootNodeG[K comparable, V any]() *nodeG[K, V] {
root := &nodeG[K, V]{}
root.prev = root
root.next = root
return root
}

func newNodeG[K comparable, V any](prev *nodeG[K, V], next *nodeG[K, V], key K, value V) *nodeG[K, V] {
return &nodeG[K, V]{prev: prev, next: next, key: key, value: value}
}

func (n *nodeG[K, V]) kvpair() *KVPairG[K, V] {
return &KVPairG[K, V]{Key: n.key, Value: n.value}
}
117 changes: 117 additions & 0 deletions ordered_map_generics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//go:build go1.18
// +build go1.18

package ordered_map

import (
"fmt"
)

type OrderedMapG[K comparable, V any] struct {
mapper map[K]*nodeG[K, V]
root *nodeG[K, V]
}

func NewOrderedMapG[K comparable, V any]() *OrderedMapG[K, V] {
om := &OrderedMapG[K, V]{
mapper: make(map[K]*nodeG[K, V]),
root: newRootNodeG[K, V](),
}
return om
}

func NewOrderedMapGWithArgs[K comparable, V any](args []*KVPairG[K, V]) *OrderedMapG[K, V] {
om := NewOrderedMapG[K, V]()
om.update(args)
return om
}

func (om *OrderedMapG[K, V]) update(args []*KVPairG[K, V]) {
for _, pair := range args {
om.Set(pair.Key, pair.Value)
}
}

func (om *OrderedMapG[K, V]) Set(key K, value V) {
if n, ok := om.mapper[key]; ok {
n.value = value
}

root := om.root
last := root.prev
n := newNodeG(last, root, key, value)
last.next = n
root.prev = n
om.mapper[key] = n
}

func (om *OrderedMapG[K, V]) Get(key K) (value V, ok bool) {
if n, ok := om.mapper[key]; ok {
return n.value, true
}
return
}

func (om *OrderedMapG[K, V]) Delete(key K) {
n, ok := om.mapper[key]
if ok {
n.prev.next = n.next
n.next.prev = n.prev
delete(om.mapper, key)
}
}

func (om *OrderedMapG[K, V]) String() string {
builder := make([]string, len(om.mapper))

var index int = 0
iter := om.IterFunc()
for kv, ok := iter(); ok; kv, ok = iter() {
val, _ := om.Get(kv.Key)
builder[index] = fmt.Sprintf("%v:%v", kv.Key, val)
index++
}
return fmt.Sprintf("OrderedMap%v", builder)
}

func (om *OrderedMapG[K, V]) Iter() <-chan *KVPairG[K, V] {
println("Iter() method is deprecated!. Use IterFunc() instead.")
return om.UnsafeIter()
}

/*
Beware, Iterator leaks goroutines if we do not fully traverse the map.
For most cases, `IterFunc()` should work as an iterator.
*/
func (om *OrderedMapG[K, V]) UnsafeIter() <-chan *KVPairG[K, V] {
keys := make(chan *KVPairG[K, V])
go func() {
defer close(keys)
var curr *nodeG[K, V]
root := om.root
curr = root.next
for curr != root {
keys <- curr.kvpair()
curr = curr.next
}
}()
return keys
}

func (om *OrderedMapG[K, V]) IterFunc() func() (*KVPairG[K, V], bool) {
var curr *nodeG[K, V]
root := om.root
curr = root.next
return func() (kvpair *KVPairG[K, V], ok bool) {
for curr != root {
kvpair = curr.kvpair()
curr = curr.next
return kvpair, true
}
return
}
}

func (om *OrderedMapG[K, V]) Len() int {
return len(om.mapper)
}
Loading