I have a function which checks if array lengths are the same:
func checkArrayLengthsMatch(desiredLength int, arrays ...[]any) bool {
for _, array := range arrays {
if len(array) != desiredLength {
return false
}
}
return true
}
But this doesn't work because of the []any.
array1 := []string{"1", "2"}
array2 := []int{1, 2}
checkArrayLengthsMatch(2, array1, array2)
gives the error cannot use array1 (variable of type []string) as type []any in argument to checkArrayLengthsMatch
Is there any way to achieve this functionality without having to write a long if statement? (e.g. if len(array1) != 2 || len(array2) != 2) Generics?
I want to be able to support checking arrays of various types.
Go playground link: https://go.dev/play/p/yKisBIOZktI
If you need to pass different types of arrays to the same function, generics won't work, but reflection will:
func checkArrayLengthsMatch(desiredLength int, arrays ...any) bool {
for _, array := range arrays {
if reflect.ValueOf(array).Len() != desiredLength {
return false
}
}
return true
}
This will also work for maps, channels, and strings.
Related
I know you can create a struct with a literal, listing the fields in order:
type Foo struct {
A string
B string
C string
}
foo := Foo{ "foo", "bar", "baz" }
Is there any way to do the same thing dynamically? I have an array of values (actually an array of arrays) and I want to assign them to an array of structs in field order, and there are rather more than three fields. Is there a way to say "assign this struct's fields from this array of values in order"? I really don't want to write a bunch of structArray[i].field1 = dataArray[i][0]; structArray[i].field2 = dataArray[i][1], etc.
My thoughts so far have been to use reflect, which seems overkillish, or maybe to create an array of field names and build json strings and unmarshal them. Any better ideas?
With reflection you can write a function like this:
func populate(dst any, src any) {
v := reflect.ValueOf(dst)
if v.Type().Kind() != reflect.Pointer {
panic("dst must be a pointer")
}
v = v.Elem()
if v.Type().Kind() != reflect.Struct {
panic("dst must be a pointer to struct")
}
w := reflect.ValueOf(src)
if w.Type().Kind() != reflect.Slice {
panic("src must be a slice")
}
for i := 0; i < v.NumField(); i++ {
// in case you need to support source slices of arbitrary types
value := w.Index(i)
if value.Type().Kind() == reflect.Interface {
value = value.Elem()
}
v.Field(i).Set(value)
}
}
You must make sure that dst is addressable, hence pass a pointer to Foo into populate; and that the i-th element in the source slice is actually assignable to the corresponding i-th field in the struct.
The code above is in a quite simplified form. You can add additional checks to it, e.g. with CanAddr or AssignableTo, if you think callers may misbehave.
Call it like:
func main() {
f := Foo{}
populate(&f, []string{"foo", "bar", "baz"})
fmt.Println(f) // {foo bar baz}
}
Here's a playground that also shows that you can pass a slice of []any as the source slice, in case the struct fields aren't all the same type: https://go.dev/play/p/G8qjDCt79C7
I know that for a single variable x, to check if it is of a certain type B, just do
switch b.(type) {
case *B:
fmt.Println("find it!")
default:
fmt.Println("can't find it")
}
But now I have a slice of 4 variables, and I'd like to know if their types follow a certain pattern (e.g. of type A,B,C,D).
I know I can do it with a tedious forloop, with many ifs and cases wrapping together, but I wonder if there's a more elegant way to achieve what I want.
You could use reflect against some "truth" slice that you define. This function will take in 2 slices and compare their types, returning an error if the types do not match in the same order.
So arr is your []interface{} slice.
exp is the expected slice, such as
// The values don't matter, only the type for the "truth" slice.
exp := []interface{}{int(0), "", Foo{}, Bar{}}
See https://goplay.tools/snippet/5nja8M00DSt
// SameTypes will compare 2 slices. If the slices have a different length,
// or any element is a different type in the same index, the function will return
// an error.
func SameTypes(arr, exps []interface{}) error {
if len(arr) != len(exps) {
return errors.New("slices must be the same length")
}
for i := range arr {
exp := reflect.TypeOf(exps[i])
found := reflect.TypeOf(arr[i])
if found != exp {
return fmt.Errorf("index '%d' expected type %s, got %s", i, exp, found)
}
}
return nil
}
Keep in mind Foo{} and &Foo{} are different types. If you don't care if it's a pointer, you will have to do additional reflect code. You can do this to get the value of the ptr if the type is a pointer.
x := &Foo{}
t := reflect.TypeOf(x)
// If t is a pointer, we deference that pointer
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
// t is now of type Foo
In my code, I need a function that would return an ordered slice of keys from a map.
m1 := make(map[string]string)
m2 := make(map[string]int)
And now I need to call a function passing both types of maps:
keys1 := sortedKeys(m1)
keys2 := sortedKeys(m1)
Problem: I have to write two functions because the function should consume maps of two different types. At the same time, the body of the function will be the same in both cases.
Question: How can I use a single implementation for two maps? Or is there any other way of solving the problem in an elegant way?
My first idea was to use map[string]interface{} as an argument type, but you can't assign neither map[string]string, nor map[string]int to it.
My code:
func sortedKeys(m map[string]string) []string {
var keys []string
for key := range m {
keys = append(keys, key)
}
sort.Strings(keys)
return keys
}
I would have to repeat the same code but for map[string]int.
You can use interface{} and use reflection for achieving this.
You can write two functions for the same but it is just not scalable, say, you are supporting string and int now but you wish to support int64, float64, bool or struct in the future. Having a common function using map[string]interface{} and using reflection is the way to go.
Suggested Code :
package main
import (
"fmt"
"reflect"
)
func main() {
m1 := make(map[string]string)
m2 := make(map[string]int)
m1["a"] = "b"
m1["b"] = "c"
m2["a"] = 1
m2["b"] = 2
fmt.Println(sortedKeys(m1))
fmt.Println(sortedKeys(m2))
}
// Returns slice of values in the type which is sent to it
func sortedKeys(m interface{}) interface{} {
if m == nil {
return nil
}
if reflect.TypeOf(m).Kind() != reflect.Map {
return nil
}
mapIter := reflect.ValueOf(m).MapRange()
mapVal := reflect.ValueOf(m).Interface()
typ := reflect.TypeOf(mapVal).Elem()
outputSlice := reflect.MakeSlice(reflect.SliceOf(typ), 0, 0)
for mapIter.Next() {
outputSlice = reflect.Append(outputSlice, mapIter.Value())
}
return outputSlice.Interface()
}
Output :
[b c]
[1 2]
https://play.golang.org/p/2fkpydH9idG
I'm having a problem with creating an empty map and append new data to it while looping on another map.
this is the error i'm getting on my IDE.
here's my data struct to be added to the map.
type Outcome struct {
QuestionIndex string
ChoiceIndex int64
Correct bool
}
func createEntryOutcome(e *entry.Entry) map[string]interface{} {
entryPicks := e.Live.Picks
outcomes := make(map[string]interface{})
for idx, pick := range entryPicks {
mappedPick := pick.(map[string]interface{})
outcomes = append(outcomes, Outcome{
QuestionIndex: idx,
ChoiceIndex: mappedPick["index"].(int64),
Correct: mappedPick["correct"].(bool),
})
}
return outcomes
}
i basically want something like below to be saved in the database.
[
{
qIndex: "1",
cIndex: 1,
correct: false,
},
{
qIndex: "1",
cIndex: 1,
correct: false,
},
]
im new to golang and any help is appreciated. thanks
As the error clearly says:
first argument to append must be slice; have map[string]interface{}
which means you need to create a slice before appending the data to outcomes which is actually slice of outcomes, like you have mentioned in the output you want.
The append function appends the elements x to the end of the slice s,
and grows the slice if a greater capacity is needed.
Create a slice of outcomes and then append the data from entryPicks to that slice:
outcomes := make([]map[string]interface{})
for idx, pick := range entryPicks {
mappedPick := pick.(map[string]interface{})
outcomes = append(outcomes, Outcome{
QuestionIndex: idx,
ChoiceIndex: mappedPick["index"].(int64),
Correct: mappedPick["correct"].(bool),
})
}
which will let you provide the outcome you want.
type Outcome struct {
QuestionIndex string
ChoiceIndex int64
Correct bool
}
func createEntryOutcome(e *entry.Entry) map[string]interface{} {
entryPicks := e.Live.Picks
var outcomes []Outcome
for idx, pick := range entryPicks {
mappedPick := pick.(map[string]interface{})
outcomes = append(outcomes, Outcome{
QuestionIndex: idx,
ChoiceIndex: mappedPick["index"].(int64),
Correct: mappedPick["correct"].(bool),
})
}
return outcomes
}
change outcomes := make(map[string]interface{}) to var outcomes []Outcome
I want to know is there a generic way to write code to judge whether a slice contains an element, I find it will frequently useful since there is a lot of logic to fist judge whether specific elem is already in a slice and then decide what to do next. But there seemed not a built-in method for that(For God's sake, why?)
I try to use interface{} to do that like:
func sliceContains(slice []interface{}, elem interface{}) bool {
for _, item := range slice {
if item == elem {
return true
}
}
return false
}
I thought interface{} is sort of like Object of Java, but apparently, I was wrong. Should I write this every time meet with a new struct of slice? Isn't there a generic way to do this?
You can do it with reflect, but it will be MUCH SLOWER than a non-generic equivalent function:
func Contains(slice, elem interface{}) bool {
sv := reflect.ValueOf(slice)
// Check that slice is actually a slice/array.
// you might want to return an error here
if sv.Kind() != reflect.Slice && sv.Kind() != reflect.Array {
return false
}
// iterate the slice
for i := 0; i < sv.Len(); i++ {
// compare elem to the current slice element
if elem == sv.Index(i).Interface() {
return true
}
}
// nothing found
return false
}
func main(){
si := []int {3, 4, 5, 10, 11}
ss := []string {"hello", "world", "foo", "bar"}
fmt.Println(Contains(si, 3))
fmt.Println(Contains(si, 100))
fmt.Println(Contains(ss, "hello"))
fmt.Println(Contains(ss, "baz"))
}
How much slower? about x50-x60 slower:
Benchmarking against a non generic function of the form:
func ContainsNonGeneic(slice []int, elem int) bool {
for _, i := range slice {
if i == elem {
return true
}
}
return false
}
I'm getting:
Generic: N=100000, running time: 73.023214ms 730.23214 ns/op
Non Generic: N=100000, running time: 1.315262ms 13.15262 ns/op
You can make it using the reflect package like that:
func In(s, e interface{}) bool {
slice, elem := reflect.ValueOf(s), reflect.ValueOf(e)
for i := 0; i < slice.Len(); i++ {
if reflect.DeepEqual(slice.Index(i).Interface(), elem.Interface()) {
return true
}
}
return false
}
Playground examples: http://play.golang.org/p/TQrmwIk6B4
Alternatively, you can:
define an interface and make your slices implement it
use maps instead of slices
just write a simple for loop
What way to choose depends on the problem you are solving.
I'm not sure what your specific context is, but you'll probably want to use a map to check if something already exists.
package main
import "fmt"
type PublicClassObjectBuilderFactoryStructure struct {
Tee string
Hee string
}
func main() {
// Empty structs occupy zero bytes.
mymap := map[interface{}]struct{}{}
one := PublicClassObjectBuilderFactoryStructure{Tee: "hi", Hee: "hey"}
two := PublicClassObjectBuilderFactoryStructure{Tee: "hola", Hee: "oye"}
three := PublicClassObjectBuilderFactoryStructure{Tee: "hi", Hee: "again"}
mymap[one] = struct{}{}
mymap[two] = struct{}{}
// The underscore is ignoring the value, which is an empty struct.
if _, exists := mymap[one]; exists {
fmt.Println("one exists")
}
if _, exists := mymap[two]; exists {
fmt.Println("two exists")
}
if _, exists := mymap[three]; exists {
fmt.Println("three exists")
}
}
Another advantage of using maps instead of a slice is that there is a built-in delete function for maps. https://play.golang.org/p/dmSyyryyS8
If you want a rather different solution, you might try the code-generator approach offered by tools such as Gen. Gen writes source code for each concrete class you want to hold in a slice, so it supports type-safe slices that let you search for the first match of an element.
(Gen also offers a few other kinds of collection and allows you to write your own.)