expr/dict-iterator.go

210 lines
4.4 KiB
Go

// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// dict-iterator.go
package expr
import (
"fmt"
"io"
"slices"
"strings"
)
type dictIterMode int
const (
dictIterModeKeys dictIterMode = iota
dictIterModeValues
dictIterModeItems
)
type DictIterator struct {
a *DictType
count int
index int
keys []any
iterMode dictIterMode
}
type sortType int
const (
sortTypeNone sortType = iota
sortTypeAsc
sortTypeDesc
sortTypeDefault = sortTypeAsc
)
func (it *DictIterator) makeKeys(m map[any]any, sort sortType) {
it.keys = make([]any, 0, len(m))
if sort == sortTypeNone {
for keyAny := range m {
it.keys = append(it.keys, keyAny)
}
} else {
scalarMap := make(map[string]any, len(m))
scalerKeys := make([]string, 0, len(m))
for keyAny := range m {
keyStr := fmt.Sprint(keyAny)
scalarMap[keyStr] = keyAny
scalerKeys = append(scalerKeys, keyStr)
}
switch sort {
case sortTypeAsc:
slices.Sort(scalerKeys)
case sortTypeDesc:
slices.Sort(scalerKeys)
slices.Reverse(scalerKeys)
}
for _, keyStr := range scalerKeys {
it.keys = append(it.keys, scalarMap[keyStr])
}
}
}
func NewDictIterator(dict *DictType, args []any) (it *DictIterator, err error) {
var sortType = sortTypeNone
var s string
it = &DictIterator{a: dict, count: 0, index: -1, keys: nil, iterMode: dictIterModeKeys}
if len(args) > 0 {
if s, err = ToGoString(args[0], "sort type"); err == nil {
switch strings.ToLower(s) {
case "a", "asc":
sortType = sortTypeAsc
case "d", "desc":
sortType = sortTypeDesc
case "n", "none", "nosort", "no-sort":
sortType = sortTypeNone
case "", "default":
sortType = sortTypeDefault
default:
err = fmt.Errorf("invalid sort type %q", s)
}
if err == nil && len(args) > 1 {
if s, err = ToGoString(args[1], "iteration mode"); err == nil {
switch strings.ToLower(s) {
case "k", "key", "keys":
it.iterMode = dictIterModeKeys
case "v", "value", "values":
it.iterMode = dictIterModeValues
case "i", "item", "items":
it.iterMode = dictIterModeItems
case "", "default":
it.iterMode = dictIterModeKeys
default:
err = fmt.Errorf("invalid iteration mode %q", s)
}
}
}
}
}
it.makeKeys(*dict, sortType)
return
}
func NewMapIterator(m map[any]any) (it *DictIterator) {
it = &DictIterator{a: (*DictType)(&m), count: 0, index: -1, keys: nil}
it.makeKeys(m, sortTypeNone)
return
}
func (it *DictIterator) String() string {
var l = 0
if it.a != nil {
l = len(*it.a)
}
return fmt.Sprintf("$(#%d)", l)
}
func (it *DictIterator) TypeName() string {
return "DictIterator"
}
func (it *DictIterator) HasOperation(name string) bool {
// yes := name == NextName || name == ResetName || name == IndexName || name == CountName || name == CurrentName
yes := slices.Contains([]string{NextName, ResetName, IndexName, CountName, CurrentName, CleanName, KeyName, ValueName}, name)
return yes
}
func (it *DictIterator) CallOperation(name string, args map[string]any) (v any, err error) {
switch name {
case NextName:
v, err = it.Next()
case ResetName:
err = it.Reset()
case CleanName:
err = it.Clean()
case IndexName:
v = int64(it.Index())
case CurrentName:
v, err = it.Current()
case CountName:
v = it.count
case KeyName:
if it.index >= 0 && it.index < len(it.keys) {
v = it.keys[it.index]
} else {
err = io.EOF
}
case ValueName:
if it.index >= 0 && it.index < len(it.keys) {
a := *(it.a)
v = a[it.keys[it.index]]
} else {
err = io.EOF
}
default:
err = errNoOperation(name)
}
return
}
func (it *DictIterator) Current() (item any, err error) {
if it.index >= 0 && it.index < len(it.keys) {
switch it.iterMode {
case dictIterModeKeys:
item = it.keys[it.index]
case dictIterModeValues:
a := *(it.a)
item = a[it.keys[it.index]]
case dictIterModeItems:
a := *(it.a)
pair := []any{it.keys[it.index], a[it.keys[it.index]]}
item = newList(pair)
}
} else {
err = io.EOF
}
return
}
func (it *DictIterator) Next() (item any, err error) {
it.index++
if item, err = it.Current(); err != io.EOF {
it.count++
}
return
}
func (it *DictIterator) Index() int {
return it.index
}
func (it *DictIterator) Count() int {
return it.count
}
func (it *DictIterator) Reset() error {
it.index = -1
it.count = 0
return nil
}
func (it *DictIterator) Clean() error {
return nil
}