From d7ba131e756057307499fb2c3349c43e8e2fd38c Mon Sep 17 00:00:00 2001 From: George Abbott Date: Wed, 1 Nov 2023 22:31:09 +0000 Subject: Commit all --- util.go | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 util.go (limited to 'util.go') diff --git a/util.go b/util.go new file mode 100644 index 0000000..3db88f0 --- /dev/null +++ b/util.go @@ -0,0 +1,175 @@ +package saggytrousers + +import ( + "fmt" + "strings" + "errors" + "reflect" + "math" + "strconv" +) + +// Given a map, return all the values as an array. +func GetMapValues[K comparable, V any](m map[K]V) []V { + vs := []V{} + for _, v := range m { + vs = append(vs, v) + } + return vs +} + +// Check if a given value is found in a slice. +func InSlice[T comparable](s []T, v T) bool { + for _, sv := range s { + if sv == v { + return true + } + } + return false +} + +// A concrete instantiation for any since any does not implement comparable. +func InSliceAny(s []any, v any) bool { + for _, sv := range s { + if sv == v { + return true + } + } + return false +} +func SliceEq[T comparable](fst, snd []T) bool { + if len(fst) != len(snd) { + return false + } + + for i := 0; i < len(fst); i++ { + if fst[i] != snd[i] { + return false + } + } + return true +} + + +// Create a filepath, consisting of a directory, a subdirectory within that +// called the `saveDir`, a filename which may contain an instance of `%s`, and +// a key which replaces the %s in the filename. +func CreateFilepath(dir, saveDir, filename, key string) string { + name := CreateFilename(filename, key) + // Add directory + ret := fmt.Sprintf("%s/%s/%s", dir, saveDir, name) + return ret +} + +func CreateFilename(filename, key string) string { + name := fmt.Sprintf(filename, key) + + // Remove illegal characters + name = strings.TrimRight(name, "\n") + name = strings.ReplaceAll(name, " ", "-") + name = strings.ReplaceAll(name, "/", "-") + name = strings.ReplaceAll(name, "\\", "-") + name = strings.ReplaceAll(name, ":", "-") + name = strings.ReplaceAll(name, ";", "-") + name = strings.ReplaceAll(name, "!", "-") + name = strings.ReplaceAll(name, "?", "-") + + return name +} + +// Take input of CreateFilename or something similar, and trim it to 20 for +// making a sheet name. +func MakeValidSheetName(s string) string { + var max int + if len(s) < 20 { + max = len(s) + } else { + max = 20 + } + return s[0:max] +} + +// Downcasts a []any to a []T. Returns error if any fail. +func Downcast[T any](vs []any) ([]T, error) { + var ret []T + for i, v := range vs { + cv, ok := v.(T) + if !ok { + errmesg := fmt.Sprintf("Downcast: failed conv at idx %v for value %v type %T", i, v, v) + return ret, errors.New(errmesg) + } + ret = append(ret, cv) + } + return ret, nil +} + +func DowncastF64(vs []any) ([]float64, error) { + var ret []float64 + for i, v := range vs { + cv, err := ConvertF64(v) + if err != nil { + errmesg := fmt.Sprintf("Downcast: failed conv at idx %v for value %v type %T", i, v, v) + return ret, errors.New(errmesg) + } + ret = append(ret, cv) + } + return ret, nil +} + +// A conversion function to f64 from any. Why this isn't in the standard god +// knows. From StackOverflow question 20767724. +var floatType = reflect.TypeOf(float64(0)) +var stringType = reflect.TypeOf("") +func ConvertF64(unkn any) (float64, error) { + switch i := unkn.(type) { + case float64: + return i, nil + case float32: + return float64(i), nil + case int64: + return float64(i), nil + case int32: + return float64(i), nil + case int: + return float64(i), nil + case uint64: + return float64(i), nil + case uint32: + return float64(i), nil + case uint: + return float64(i), nil + case string: + return strconv.ParseFloat(i, 64) + default: + v := reflect.ValueOf(unkn) + v = reflect.Indirect(v) + if v.Type().ConvertibleTo(floatType) { + fv := v.Convert(floatType) + return fv.Float(), nil + } else if v.Type().ConvertibleTo(stringType) { + sv := v.Convert(stringType) + s := sv.String() + return strconv.ParseFloat(s, 64) + } else { + return math.NaN(), fmt.Errorf("Can't convert %v to float64", v.Type()) + } + } +} + +// Get a given _column_ from a set of rows. This is used to get columns from +// GetRows, which is potentially faster than calling GetColumns. +func TransmogrifyRows(rows [][]any, index int) []any { + var ret []any + for _, row := range rows { + ret = append(ret, row[index]) + } + return ret +} + +// Process a string, removing spaces, making all lowercase, etc. so that we get +// easily `switch`-able strings. +func ProcessStr(str string) string { + ret := strings.TrimSuffix(str, " ") + ret = strings.ToLower(str) + return ret +} -- cgit v1.2.1