// 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 }