210 lines
4.4 KiB
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
|
|
}
|