// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. // dict-iterator.go package expr import ( "fmt" "io" "slices" "strings" "git.portale-stac.it/go-pkg/expr/kern" ) type dictIterMode int const ( dictIterModeKeys dictIterMode = iota dictIterModeValues dictIterModeItems ) type DictIterator struct { a *kern.DictType count int64 index int64 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 *kern.DictType, args []any) (it *DictIterator, err error) { var sortType = sortTypeNone var s string var argAny any dictIt := &DictIterator{a: dict, count: 0, index: -1, keys: nil, iterMode: dictIterModeKeys} if len(args) > 0 { argAny = args[0] } else { argAny = "default" } if s, err = kern.ToGoString(argAny, "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 { if len(args) > 1 { argAny = args[1] } else { argAny = "default" } if s, err = kern.ToGoString(argAny, "iteration mode"); err == nil { switch strings.ToLower(s) { case "k", "key", "keys": dictIt.iterMode = dictIterModeKeys case "v", "value", "values": dictIt.iterMode = dictIterModeValues case "i", "item", "items": dictIt.iterMode = dictIterModeItems case "", "default": dictIt.iterMode = dictIterModeKeys default: err = fmt.Errorf("invalid iteration mode %q", s) } } } } dictIt.makeKeys(*dict, sortType) return dictIt, err } func NewMapIterator(m map[any]any) (it *DictIterator) { it = &DictIterator{a: (*kern.DictType)(&m), count: 0, index: -1, keys: nil} it.makeKeys(m, sortTypeNone) return } func (it *DictIterator) String() string { var l = int64(0) if it.a != nil { l = int64(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{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName, kern.KeyName, kern.ValueName}, name) return yes } func (it *DictIterator) CallOperation(name string, args map[string]any) (v any, err error) { switch name { case kern.NextName: v, err = it.Next() case kern.ResetName: err = it.Reset() case kern.CleanName: err = it.Clean() case kern.IndexName: v = int64(it.Index()) case kern.CurrentName: v, err = it.Current() case kern.CountName: v = it.count case kern.KeyName: if it.index >= 0 && it.index < int64(len(it.keys)) { v = it.keys[it.index] } else { err = io.EOF } case kern.ValueName: if it.index >= 0 && it.index < int64(len(it.keys)) { a := *(it.a) v = a[it.keys[it.index]] } else { err = io.EOF } default: err = kern.ErrNoOperation(name) } return } func (it *DictIterator) Current() (item any, err error) { if it.index >= 0 && it.index < int64(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 = kern.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() int64 { return it.index } func (it *DictIterator) Count() int64 { return it.count } func (it *DictIterator) Reset() error { it.index = -1 it.count = 0 return nil } func (it *DictIterator) Clean() error { return nil }