ordmap

package module
v0.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 11, 2023 License: MIT Imports: 1 Imported by: 0

README

go-ordmap

main pipeline Go Report Card GoDoc

Persistent generic ordered maps for Go.

This is the code-generating version, for generics-based version (Go 1.18+) see v2.

Rationale

Standard library does not provide a key-value store data structure that would also allow quick access to minimum/maximum and in-order iteration.

This package provides such data structure (called OrdMap for "ordered map") and implements it on top of AVL trees (self balancing binary trees). This allows us O(log n) read/write access to arbitrary key as well as to the min & max while still supporting O(n) iteration over elements - just that it's now in order.

One detail that may not be idiomatic for Go is that these OrdMaps are persistent data structures - meaning that each operation gives you back a new map while keeping the old intact. This is beneficial when you have many concurrent readers - your writer can advance while the readers still traverse the old versions (kind of similar to MVCC)

In order to facilitate safe API and efficient internalization the actual code is actually generated from a template and specialised to your types. Think "generics" - inspired by genny but tweaked to this particular case.

Usage

The recommended usage is through code generation. Place this comment in one of your .go files

//go:generate go run github.com/edofic/go-ordmap/cmd/gen -name IntStrMap -key intKey -value string -target ./int_str_map.go

NOTE go-ormap uses go:embed internally so this requires at least Go 1.16 (otherwise you will get a compilation failure)

And run go generate ./... (more about generation)

You need to provide the types (if not stdlib) that are referenced. Mostly you'll need to implement the key type

type intKey int

func (i intKey) Less(other intKey) bool {
	return int(i) < int(other)
}

You will need to provide the Less method - so the map knows how to order itself. Then you can use the generated map type.

Using the map

You only need to remember to always assign the returned value of all operations; the original object is never updated in-place:

var m1 *IntStrMap                // zero value is the empty map
m1 = m1.Insert(intKey(1), "foo") // adding entries
m1 = m1.Insert(intKey(2), "baz")
m1 = m1.Insert(intKey(2), "bar") // will override
fmt.Println(m1.Get(intKey(2)))   // access by key
m1 = m1.Insert(intKey(3), "baz")
// this is how you can efficiently iterate in-order
for i := m1.Iterate(); !i.Done(); i.Next() {
    fmt.Println(i.GetKey(), i.GetValue())
}
m1 = m1.Remove(intKey(1)) // can also remove entries
fmt.Println(m1.Entries()) // or get a slice of all of them
// can iterate in reverse as well
for i := m1.IterateReverse(); !i.Done(); i.Next() {
    fmt.Println(i.GetKey(), i.GetValue())
}
fmt.Println(m1.Min(), m1.Max()) // access the extremes

See examples/gen/main.go for a fully functioning example.

Using raw version

If for some reason you don't want to use code generation you can directly use the template implementation. This less efficient and less safe as it uses an interface for keys and an empty interface for values.

import "github.com/edofic/go-ordmap"

Key will now need to compare against the ordmap.Key interface type (less safe)

type intKey int

func (i intKey) Less(other ordmap.Key) bool {
	// unsafe, this can blow up at runtime if you mix key types
	return int(i) < int(other.(intKey))
}

Usage then is virtual the same - except for the interface{} values

var m *ordmap.OrdMap
m = m.Insert(intKey(1), "foo")
m = m.Insert(intKey(2), "baz")
m = m.Insert(intKey(2), "bar")
m = m.Insert(intKey(3), "baz")
for i := m.Iterate(); !i.Done(); i.Next() {
    // value here is interface{}
    fmt.Println(i.GetKey(), i.GetValue())
}

See examples/raw/main.go for a fully functioning example.

Development

Go 1.16+ required due to use of go:embed.

Generation

If you're changing the template avl.go make sure to run go generate ./... to update generated code in examples.

Testing

Standard testing

go test ./...

100% test coverage expected on the template (avl.go)

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Template string

Functions

This section is empty.

Types

type Entry

type Entry struct {
	K Key
	V Value
}

type Iterator

type Iterator struct {
	// contains filtered or unexported fields
}

func (*Iterator) Done

func (i *Iterator) Done() bool

func (*Iterator) GetKey

func (i *Iterator) GetKey() Key

func (*Iterator) GetValue

func (i *Iterator) GetValue() Value

func (*Iterator) Next

func (i *Iterator) Next()

type Key

type Key interface {
	Less(Key) bool
}

type OrdMap

type OrdMap struct {
	Entry Entry
	// contains filtered or unexported fields
}

func (*OrdMap) Entries

func (node *OrdMap) Entries() []Entry

func (*OrdMap) Get

func (node *OrdMap) Get(key Key) (value Value, ok bool)

func (*OrdMap) Height

func (node *OrdMap) Height() int

func (*OrdMap) Insert

func (node *OrdMap) Insert(key Key, value Value) *OrdMap

func (*OrdMap) Iterate

func (node *OrdMap) Iterate() Iterator

func (*OrdMap) IterateFrom

func (node *OrdMap) IterateFrom(k Key) Iterator

func (*OrdMap) IterateReverse

func (node *OrdMap) IterateReverse() Iterator

func (*OrdMap) IterateReverseFrom

func (node *OrdMap) IterateReverseFrom(k Key) Iterator

func (*OrdMap) Len

func (node *OrdMap) Len() int

func (*OrdMap) Max

func (node *OrdMap) Max() *Entry

func (*OrdMap) Min

func (node *OrdMap) Min() *Entry

func (*OrdMap) Remove

func (node *OrdMap) Remove(key Key) *OrdMap

type Value

type Value interface{}

Directories

Path Synopsis
cmd
gen
examples
composite_index
DO NOT EDIT this code was generated using go-ordmap code generation go run github.com/edofic/go-ordmap/cmd/gen -name Index -key CompositeKey -value bool -target ./composite.go
DO NOT EDIT this code was generated using go-ordmap code generation go run github.com/edofic/go-ordmap/cmd/gen -name Index -key CompositeKey -value bool -target ./composite.go
gen
DO NOT EDIT this code was generated using go-ordmap code generation go run github.com/edofic/go-ordmap/cmd/gen -name IntIntMap -key int -less < -value int -target ./int_int_map.go
DO NOT EDIT this code was generated using go-ordmap code generation go run github.com/edofic/go-ordmap/cmd/gen -name IntIntMap -key int -less < -value int -target ./int_int_map.go
ptr_type
DO NOT EDIT this code was generated using go-ordmap code generation go run github.com/edofic/go-ordmap/cmd/gen -name MyMap -key int -less < -value *MyValue -target ./my_map.go
DO NOT EDIT this code was generated using go-ordmap code generation go run github.com/edofic/go-ordmap/cmd/gen -name MyMap -key int -less < -value *MyValue -target ./my_map.go
raw

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL