Files
libopenapi/orderedmap/orderedmap.go
2024-08-26 20:42:40 +01:00

325 lines
6.6 KiB
Go

// Ordered map container
// Works like the Golang `map` built-in, but preserves order that key/value
// pairs were added when iterating.
package orderedmap
import (
"context"
"fmt"
"iter"
"reflect"
"slices"
"strings"
wk8orderedmap "github.com/wk8/go-ordered-map/v2"
)
// Pair represents a key/value pair in an ordered map returned for iteration.
type Pair[K comparable, V any] interface {
Key() K
KeyPtr() *K
Value() V
ValuePtr() *V
Next() Pair[K, V]
}
// Map represents an ordered map where the key must be a comparable type, the ordering is based on insertion order.
type Map[K comparable, V any] struct {
*wk8orderedmap.OrderedMap[K, V]
}
type wrapPair[K comparable, V any] struct {
*wk8orderedmap.Pair[K, V]
}
// New creates an ordered map generic object.
func New[K comparable, V any]() *Map[K, V] {
return &Map[K, V]{
OrderedMap: wk8orderedmap.New[K, V](),
}
}
// GetKeyType returns the reflection type of the key.
func (o *Map[K, V]) GetKeyType() reflect.Type {
return reflect.TypeOf(new(K))
}
// GetValueType returns the reflection type of the value.
func (o *Map[K, V]) GetValueType() reflect.Type {
return reflect.TypeOf(new(V))
}
// GetOrZero will return the value for the key if it exists, otherwise it will return the zero value for the value type.
func (o *Map[K, V]) GetOrZero(k K) V {
v, ok := o.OrderedMap.Get(k)
if !ok {
var zero V
return zero
}
return v
}
// First returns the first pair in the map useful for iteration.
func (o *Map[K, V]) First() Pair[K, V] {
if o == nil {
return nil
}
pair := o.OrderedMap.Oldest()
if pair == nil {
return nil
}
return &wrapPair[K, V]{
Pair: pair,
}
}
// FromOldest returns an iterator that yields the oldest key-value pair in the map.
func (o *Map[K, V]) FromOldest() iter.Seq2[K, V] {
return func(yield func(K, V) bool) {
if o == nil {
return
}
for k, v := range o.OrderedMap.FromOldest() {
if !yield(k, v) {
return
}
}
}
}
// FromNewest returns an iterator that yields the newest key-value pair in the map.
func (o *Map[K, V]) FromNewest() iter.Seq2[K, V] {
o.OrderedMap.FromNewest()
return func(yield func(K, V) bool) {
if o == nil {
return
}
for k, v := range o.OrderedMap.FromNewest() {
if !yield(k, v) {
return
}
}
}
}
// FromNewest returns an iterator that yields the newest key-value pair in the map.
func (o *Map[K, V]) KeysFromOldest() iter.Seq[K] {
return func(yield func(K) bool) {
if o == nil {
return
}
for k := range o.OrderedMap.KeysFromOldest() {
if !yield(k) {
return
}
}
}
}
// KeysFromNewest returns an iterator that yields the newest key in the map.
func (o *Map[K, V]) KeysFromNewest() iter.Seq[K] {
return func(yield func(K) bool) {
if o == nil {
return
}
for k := range o.OrderedMap.KeysFromNewest() {
if !yield(k) {
return
}
}
}
}
// ValuesFromOldest returns an iterator that yields the oldest value in the map.
func (o *Map[K, V]) ValuesFromOldest() iter.Seq[V] {
return func(yield func(V) bool) {
if o == nil {
return
}
for v := range o.OrderedMap.ValuesFromOldest() {
if !yield(v) {
return
}
}
}
}
// ValuesFromNewest returns an iterator that yields the newest value in the map.
func (o *Map[K, V]) ValuesFromNewest() iter.Seq[V] {
return func(yield func(V) bool) {
if o == nil {
return
}
for v := range o.OrderedMap.ValuesFromNewest() {
if !yield(v) {
return
}
}
}
}
// From creates a new ordered map from an iterator.
func From[K comparable, V any](iter iter.Seq2[K, V]) *Map[K, V] {
return &Map[K, V]{
OrderedMap: wk8orderedmap.From(iter),
}
}
// NewPair instantiates a `Pair` object for use with `FromPairs()`.
func NewPair[K comparable, V any](key K, value V) Pair[K, V] {
return &wrapPair[K, V]{
Pair: &wk8orderedmap.Pair[K, V]{
Key: key,
Value: value,
},
}
}
// FromPairs creates an `OrderedMap` from an array of pairs.
// Use `NewPair()` to generate input parameters.
func FromPairs[K comparable, V any](pairs ...Pair[K, V]) *Map[K, V] {
om := New[K, V]()
for _, pair := range pairs {
om.Set(pair.Key(), pair.Value())
}
return om
}
// IsZero is required to support `omitempty` tag for YAML/JSON marshaling.
func (o *Map[K, V]) IsZero() bool {
return Len(o) == 0
}
// Next returns the next pair in the map when iterating.
func (p *wrapPair[K, V]) Next() Pair[K, V] {
next := p.Pair.Next()
if next == nil {
return nil
}
return &wrapPair[K, V]{
Pair: next,
}
}
// Key returns the key of the pair.
func (p *wrapPair[K, V]) Key() K {
return p.Pair.Key
}
// KeyPtr returns a pointer to the key of the pair.
func (p *wrapPair[K, V]) KeyPtr() *K {
return &p.Pair.Key
}
// Value returns the value of the pair.
func (p *wrapPair[K, V]) Value() V {
return p.Pair.Value
}
// ValuePtr returns a pointer to the value of the pair.
func (p *wrapPair[K, V]) ValuePtr() *V {
return &p.Pair.Value
}
// Len returns the length of a container implementing a `Len()` method.
// Safely returns zero on nil pointer.
func Len[K comparable, V any](m *Map[K, V]) int {
if m == nil {
return 0
}
return m.Len()
}
// Iterate the map in order.
// Safely handles nil pointer.
// Be sure to iterate to end or cancel the context when done to release
// resources.
func Iterate[K comparable, V any](ctx context.Context, m *Map[K, V]) <-chan Pair[K, V] {
c := make(chan Pair[K, V])
if Len(m) == 0 {
close(c)
return c
}
go func() {
defer close(c)
for pair := First(m); pair != nil; pair = pair.Next() {
select {
case c <- pair:
case <-ctx.Done():
return
}
}
}()
return c
}
// ToOrderedMap converts a `map` to `OrderedMap`.
func ToOrderedMap[K comparable, V any](m map[K]V) *Map[K, V] {
om := New[K, V]()
for k, v := range m {
om.Set(k, v)
}
return SortAlpha(om)
}
// First returns map's first pair for iteration.
// Safely handles nil pointer.
func First[K comparable, V any](m *Map[K, V]) Pair[K, V] {
if m == nil {
return nil
}
return m.First()
}
// Cast converts `any` to `Map`.
func Cast[K comparable, V any](v any) *Map[K, V] {
if v == nil {
return nil
}
m, ok := v.(*Map[K, V])
if !ok {
return nil
}
return m
}
// SortAlpha sorts the map by keys in alphabetical order.
func SortAlpha[K comparable, V any](m *Map[K, V]) *Map[K, V] {
if m == nil {
return nil
}
om := New[K, V]()
type key struct {
key string
k K
}
keys := []key{}
for pair := First(m); pair != nil; pair = pair.Next() {
keys = append(keys, key{
key: fmt.Sprintf("%v", pair.Key()),
k: pair.Key(),
})
}
slices.SortFunc(keys, func(a, b key) int {
return strings.Compare(a.key, b.key)
})
for _, k := range keys {
om.Set(k.k, m.GetOrZero(k.k))
}
return om
}