I have the following code to sort a slice of integer strings by the value of the integers. What would be the way to return the err1 and err2 errors in the sortSlice function, in case the error is not nil? The return of the comparator is used by the sort function, so I'm not sure how to return the error.
func (o *Object) sortSlice() error {
sort.Slice(mySlice, func(i, j int) bool {
numA, err1 := strconv.Atoi(mySlice[i])
numB, err2 := strconv.Atoi(mySlice[j])
return numA > numB
})
}
You might want to pass through that array once to convert to ints, instead of calling Atoi n^2 times:
ints:=make([]int,0,len(mySlice)
for _,x:=range mySlice {
v, err:=strconv.Atoi(x)
if err!=nil {
return err
}
ints=append(ints,v)
}
sort.Slice(mySlice,func(i,j int) bool {return ints[i]>ints[j]})
In the general case though, you might want to set an error in the outer scope:
var err error
sort.Slice(mySlice, func(i, j int) bool {
numA, err1 := strconv.Atoi(mySlice[i])
if err1!=nil && err==nil {
err=err1
}
numB, err2 := strconv.Atoi(mySlice[j])
if err2!=nil && err==nil {
err=err2
}
return numA > numB
})
return err
The above code will capture the first detected error.
Related
I have implemented a very simple Decode method (using gob.Decoder for now) - this works well for single responses - it would even work well for slices, but I need to implement a DecodeMany method where it is able to decode a set of individual responses (not a slice).
Working Decode method:
var v MyType
_ = Decode(&v)
...
func Decode(v interface{}) error {
buf, _ := DoSomething() // func DoSomething() ([]byte, error)
// error handling omitted for brevity
return gob.NewDecoder(bytes.NewReader(buf)).Decode(v)
}
What I'm trying to do for a DecodeMany method is to deal with a response that isn't necessarily a slice:
var vv []MyType
_ = DecodeMany(&vv)
...
func DecodeMany(vv []interface{}) error {
for _, g := range DoSomething() { // func DoSomething() []struct{Buf []bytes}
// Use g.Buf as an individual "interface{}"
// want something like:
var v interface{} /* Somehow create instance of single vv type? */
_ = gob.NewDecoder(bytes.NewReader(g.Buf)).Decode(v)
vv = append(vv, v)
}
return
}
Besides not compiling the above also has the error of:
cannot use &vv (value of type *[]MyType) as type []interface{} in argument to DecodeMany
If you want to modify the passed slice, it must be a pointer, else you must return a new slice. Also if the function is declared to have a param of type []interface{}, you can only pass a value of type []interface{} and no other slice types... Unless you use generics...
This is a perfect example to start using generics introduced in Go 1.18.
Change DecodeMany() to be generic, having a T type parameter being the slice element type:
When taking a pointer
func DecodeMany[T any](vv *[]T) error {
for _, g := range DoSomething() {
var v T
if err := gob.NewDecoder(bytes.NewReader(g.Buf)).Decode(&v); err != nil {
return err
}
*vv = append(*vv, v)
}
return nil
}
Here's a simple app to test it:
type MyType struct {
S int64
}
func main() {
var vv []MyType
if err := DecodeMany(&vv); err != nil {
panic(err)
}
fmt.Println(vv)
}
func DoSomething() (result []struct{ Buf []byte }) {
for i := 3; i < 6; i++ {
buf := &bytes.Buffer{}
v := MyType{S: int64(i)}
if err := gob.NewEncoder(buf).Encode(v); err != nil {
panic(err)
}
result = append(result, struct{ Buf []byte }{buf.Bytes()})
}
return
}
This outputs (try it on the Go Playground):
[{3} {4} {5}]
When returning a slice
If you choose to return the slice, you don't have to pass anything, but you need to assign the result:
func DecodeMany[T any]() ([]T, error) {
var result []T
for _, g := range DoSomething() {
var v T
if err := gob.NewDecoder(bytes.NewReader(g.Buf)).Decode(&v); err != nil {
return result, err
}
result = append(result, v)
}
return result, nil
}
Using it:
vv, err := DecodeMany[MyType]()
if err != nil {
panic(err)
}
fmt.Println(vv)
Try this one on the Go Playground.
The following code attempt to implement a generic CSV writer for any simple struct. By "simple", I mean field value of the struct are of standard, simple types (int, string etc).
type (
CSV interface {
Header() []string
String([]string) (string, error)
}
CSVArray []CSV
)
func CSVOutput(w io.Writer, data CSVArray, cols []string) error {
if len(data) == 0 {
return nil
}
_, err := fmt.Fprintln(w, data[0].Header())
if err != nil {
return err
}
for _, d := range data {
str, err := d.String(cols)
if err != nil {
return err
}
_, err = fmt.Fprintln(w, str)
if err != nil {
return err
}
}
return nil
}
The problem is CSVOutput() does not actually work. e.g.:
var data []Employee //the Employee struct implements CSV interface
CSVOutput(w, data, nil)
Compilation failed: cannot use data (type []Employee) as type CSVArray in argument to CSVOutput
I understand that []CSV is not same as []Employee, as explained here, and many other resources available online.
That said, is it possible to rewrite the CSVOutput() function by using reflection:
func CSVOutput(w io.Writer, data interfac{}, cols []string) error {
sliceOfIntf = castToSlice(data) //how to do this?
if !implementedCSV(sliceOfIntf[0]) { //and how to do this?
return errors.New("not csv")
}
... ...
}
is it possible to rewrite the CSVOutput() function by using reflection
Yes
// if data is []Employee{...}, then you can do the following:
rv := reflect.ValueOf(data)
if rv.Kind() != reflect.Slice {
return fmt.Errorf("data is not slice")
}
if !rv.Type().Elem().Implements(reflect.TypeOf((*CSV)(nil)).Elem()) {
return fmt.Errorf("slice element does not implement CSV")
}
csvArr := make(CSVArray, rv.Len())
for i := 0; i < rv.Len(); i++ {
csvArr[i] = rv.Index(i).Interface().(CSV)
}
// now csvArr is CSVArray containing all the elements of data
https://go.dev/play/p/gcSOid533gx
Context:
Using a custom Binder and Validator in Echo Framework. The binder use a signature of (interface{}, echo.Context), but a pointer is always passed and checked by echo.DefaultBinder.
I'm having trouble validating an array of struct for some reason, when an array is passed for some unknown reason. Therefore, I'm trying to validate each elements in the interface, if this interface is an Array or a Slice.
Problem:
I cannot find a way to both cast the interface to a value instead of a pointer and iterate trough the values of this array to validate each of them.
My Code so far:
func (cb *CustomBinder) Bind(i interface{}, c echo.Context) error {
db := new(echo.DefaultBinder)
validate := validator.New()
if err := db.Bind(i, c); err != nil {
return err
}
kind := reflect.ValueOf(i).Elem().Kind()
if kind == reflect.Array || kind == reflect.Slice {
// ... Iteration and Validation
} else {
if err := validate.Struct(i); err != nil {
return err
}
}
return nil
}
I would rather use type assertions than reflection because reflection is slow in terms of performance and not friendly to use.
To illustrate what I mean, check this example code where I have a function that accepts an argument of type interface{} and prints the values according to the data type,
package main
import (
"fmt"
)
func main() {
var dynamicValue interface{}
dynamicValue = []interface{}{"value1", "value2"}
printValue(dynamicValue)
dynamicValue = map[string]interface{}{"key1": "value1"}
printValue(dynamicValue)
dynamicValue = "value1"
printValue(dynamicValue)
}
func printValue(i interface{}) {
if arrayValue, isArray := i.([]interface{}); isArray {
for index, value := range arrayValue {
fmt.Printf("Index: %d Value: %v \n", index, value)
}
} else if mapValue, isMap := i.(map[string]interface{}); isMap {
for key, value := range mapValue {
fmt.Printf("Key: %s Value: %v \n", key, value)
}
} else if stringValue, isString := i.(string); isString {
fmt.Println(stringValue)
} else {
fmt.Println("Invalid data type! Only supports string, arrays and maps ")
}
}
Output:
Index: 0 Value: value1
Index: 1 Value: value2
Key: key1 Value: value1
value1
Playground: https://play.golang.org/p/TMfojVdoi5b
You can use this logic of type assertion in your code to check if the interface is a slice and iterate over it for validation.
Something like this,
func (cb *CustomBinder) Bind(i interface{}, c echo.Context) error {
db := new(echo.DefaultBinder)
validate := validator.New()
if err := db.Bind(i, c); err != nil {
return err
}
if arrayValue, isArray := i.([]interface{}); isArray {
// Iteration
for index, value := range arrayValue {
// Validation
}
} else {
if err := validate.Struct(i); err != nil {
return err
}
}
return nil
}
I'm trying to list all function call in a function using ast. But having trouble understanding how it is suppose to be used. I have been able to get this far.
set := token.NewFileSet()
packs, err := parser.ParseFile(set, serviceFile, nil, 0)
if err != nil {
fmt.Println("Failed to parse package:", err)
os.Exit(1)
}
funcs := []*ast.FuncDecl{}
for _, d := range packs.Decls {
if fn, isFn := d.(*ast.FuncDecl); isFn {
funcs = append(funcs, fn)
}
}
I have inspected funcs. I get to funcs[n1].Body.List[n2].
But after this i don't understand how i'm suppose to read the underlaying data.X.Fun.data.Sel.name (got it from evaluation in gogland) to get name of the function being called.
you can use ast.Inspect for that and use a switch on the type of the node.
for _, fun := range funcs {
ast.Inspect(fun, func(node ast.Node) bool {
switch n := node.(type) {
case *ast.CallExpr:
fmt.Println(n) // prints every func call expression
}
return true
})
}
Ok so what i found is that you have to a lot of casting to actually extract the data.
Here is an example on how to do extract the func call in a func.
for _, function := range funcs {
extractFuncCallInFunc(function.Body.List)
}
func extractFuncCallInFunc(stmts []ast.Stmt) {
for _, stmt := range funcs {
if exprStmt, ok := stmt.(*ast.ExprStmt); ok {
if call, ok := exprStmt.X.(*ast.CallExpr); ok {
if fun, ok := call.Fun.(*ast.SelectorExpr); ok {
funcName := fun.Sel.Name
}
}
}
}
}
I also found this with kind of help with finding out what you need to cast it to.
http://goast.yuroyoro.net/
Trying to understand the mentality of Go. I wrote the following function which looks for *.txt files of a folder that have a date in the filename, get the latest date and return that date.
func getLatestDate(path string) (time.Time, error) {
if fns, e := filepath.Glob(filepath.Join(path, "*.txt")); e == nil {
re, _ := regexp.Compile(`_([0-9]{8}).txt$`)
max := ""
for _, fn := range fns {
if ms := re.FindStringSubmatch(fn); ms != nil {
if ms[1] > max {
max = ms[1]
}
}
}
date, _ := time.Parse("20060102", max)
return date, nil
} else {
return time.Time{}, e
}
}
What would be the more idiomatic version of this function, if there is one?
Here is my take
Use MustCompile to compile a static regexp. This will panic if it doesn't compile and saves an error check
Hoist compiling the regexp out of the function - you only need it compiled once. Note that I've called it with a lowercase initial letter so it won't be visible outside the package.
Use an early return when checking errors - this saves indentation and is idiomatic go
Use named return parameters for those early returns - saves defining nil values for types and typing in general (not to everyone's taste though)
return time.Parse directly which checks the errors (you weren't before)
The code
var dateRe = regexp.MustCompile(`_([0-9]{8}).txt$`)
func getLatestDate(path string) (date time.Time, err error) {
fns, err := filepath.Glob(filepath.Join(path, "*.txt"))
if err != nil {
return
}
max := ""
for _, fn := range fns {
if ms := dateRe.FindStringSubmatch(fn); ms != nil {
if ms[1] > max {
max = ms[1]
}
}
}
return time.Parse("20060102", max)
}
Here's how I would have written it. Don't ignore errors, use guard clauses for error handling, and don't recompile regexps inside a loop.
var datePat = regexp.MustCompile(`_([0-9]{8}).txt$`)
func getLatestDate(path string) (time.Time, error) {
fns, err := filepath.Glob(filepath.Join(path, "*.txt"))
if err != nil {
return time.Time{}, err
}
max := time.Time{}
for _, fn := range fns {
if ms := re.FindStringSubmatch(fn); ms != nil {
if t, err := time.Parse("20060102", ms[1]); err == nil && t.After(max) {
max = t
}
}
}
return max, nil
}