Build array with different types - go

I'm very new in Go, and I came from Ruby. So...
can I build array what includes different types
[1, 2, "apple", true]
Sorry if it's stupid question.
Thanks.

You can do this by making a slice of interface{} type. For example:
func main() {
arr := []interface{}{1, 2, "apple", true}
fmt.Println(arr)
// however, now you need to use type assertion access elements
i := arr[0].(int)
fmt.Printf("i: %d, i type: %T\n", i, i)
s := arr[2].(string)
fmt.Printf("b: %s, i type: %T\n", s, s)
}
Read more about this here.

Depending on the situation, you might be able to use a struct instead:
package main
import "fmt"
type array struct {
one, two int
fruit string
truth bool
}
func main() {
arr := array{1, 2, "apple", true}
fmt.Println(arr)
}
https://golang.org/ref/spec#Struct_types

Related

what's the most efficient way to iterate over an interface{} object in Go? [duplicate]

This question already has answers here:
Iterate through the fields of a struct in Go
(8 answers)
Closed last month.
e.g. Assuming the interface{} object is a struct {"a":1, "b": "test", c: &AnotherStruct{}}, and we need to iterate the object to get value of each field "a", "b", "c".
I can think of two ways:
use Go reflection directly.
use json.Marshal()/json.Unmarshal() to convert the object to map[string]interface{}, and then iterate over the map to do type assertions, this also calls reflection, however there might be some json library having optimizations inside which might gain better performance, e.g. https://github.com/bytedance/sonic.
I was wondering which one is more efficient and is there any other way to do it?
interface{} is (legacy) go short-hand for "this could be anything". It does not represent an "object" (though it could). From go 1.18 onward the keyword any was introduced as a direct replacement for interface{} (though the latter may continue to be used if you need compatibility with older golang versions).
Here-on I shall use any for brevity.
I'd suggest ignoring efficiency unless/until it becomes a problem you need to solve and instead focus on the clearest and simplest way to achieve what you need, functionally.
It is difficult to be clear about what exactly you are faced with and trying to achieve; your "example" object contains both quoted and unquoted field members so I see four possible scenarios that you may be dealing with:
an any variable holding a value that is of a known formal struct type
an any variable holding a value that is of an anonymous formal struct type
an any variable holding a JSON string
an any variable holding a map[string]any
For scenario's 1 and 2 that would be to marshal to JSON then unmarshal to map[string]any.
For scenario 3 you would cast to string then unmarshal to map[string]any:
For scenario 4 you would directly cast to map[string]any.
I have worked up all of these in a playground for you here: https://go.dev/play/p/cSdUmynTFRp
package main
import (
"encoding/json"
"fmt"
)
type AnotherStruct struct {
X int `json:"x"`
}
type Foo struct {
A int `json:"a"`
B string `json:"b"`
C AnotherStruct `json:"c"`
}
func emit(m map[string]any) {
for k, v := range m {
fmt.Printf(" %s: %s\n", k, v)
}
fmt.Println()
}
func scenario1or2(n int, foo any) map[string]any {
fmt.Printf("scenario %d:\n", n)
j, _ := json.Marshal(foo)
m := map[string]any{}
json.Unmarshal(j, &m)
return m
}
func scenario3(foo any) map[string]any {
fmt.Println("scenario 3")
m := map[string]any{}
json.Unmarshal([]byte(foo.(string)), &m)
return m
}
func scenario4(foo any) map[string]any {
fmt.Println("scenario 4")
return foo.(map[string]any)
}
func main() {
emit(scenario1or2(1, Foo{
A: 1,
B: "test",
C: AnotherStruct{X: 42},
}))
emit(scenario1or2(2, struct {
A int
B string
C AnotherStruct
}{
A: 1,
B: "test",
C: AnotherStruct{
X: 42,
},
}))
emit(scenario3(`{"a":1,"b":"test","c":{"x":42}}`))
emit(scenario4(map[string]any{
"a": 1,
"b": "test",
"c": AnotherStruct{
X: 42,
},
}))
}
If you have scenario 1 and simply want efficient access to the fields (i.e. iterating over potentially unknown fields is not actually required) then you can type-cast directly to the known formal struct type:
// assuming...
type Foo struct {
a int
b string
c *AnotherStruct {
}
}
// and where 'anyfoo' is an `any` holding a Foo
foo := anyfoo.(Foo)
a := foo.a
b := foo.b
c := foo.c

go 1.18 generic compile error when use maps.Copy on map with struct key

I implemented a Set based on generic, and everything ok until i use struct as Set element instead of base type. I got an compliation error.
go version: go version go1.18 windows/amd64
Below code is failed to complie in function AddSet.
package main
import (
"fmt"
"golang.org/x/exp/maps"
)
type Key struct {
A, B int
}
func main() {
s := SetOf(
Key{1, 1},
Key{2, 2},
Key{3, 3},
)
s.AddSet(SetOf(
Key{3, 3},
Key{4, 4},
Key{5, 5},
))
fmt.Println(s)
}
type Set[T comparable] map[T]struct{}
func SetOf[T comparable](vs ...T) Set[T] {
s := Set[T]{}
for _, v := range vs {
s[v] = struct{}{}
}
return s
}
func (s Set[T]) AddSet(another Set[T]) {
maps.Copy(s, another)
}
when run it:
> go run .\main.go
# command-line-arguments
.\main.go:19:10: cannot use &.autotmp_29 (type *struct { A int; B int }) as type *Key in argument to runtime.mapassign
<autogenerated>:1: cannot use &.autotmp_12 (type *struct { A int; B int }) as type *Key in argument to runtime.mapassign
if Key only has 1 field, it can be compiled successful.
if i use for v := range another { s[v]=struct{}{} }, it can be compiled successful.
i think it's strange, can someone explain please?
It looks like this compiler error. It is fixed in Go 1.19 and backported to Go 1.18.2.
If you are on an older version, I'd recommend simply forgoing the maps package and doing things by hand, as you already tried. It's just a simple loop:
func (s Set[T]) AddSet(another Set[T]) {
for k := range another {
s[k] = struct{}{}
}
}
#icza's comment of explicitly converting the named map type to its underlying type also works:
maps.Copy(map[T]struct{}(s), another)
In case you use functions that expect more than one map type parameter (with the same constraints), as maps.Equal or maps.EqualFunc, you have to convert both arguments:
func (s Set[T]) Compare(another Set[T]) bool {
// signature is Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool
return maps.Equal(map[T]struct{}(s), map[T]struct{}(another))
}
It seems the crash was reproduced also with parametrized map types instantiated with arrays with len >= 2.

How to make variable type slices

I was learning go by doing some of the problems I have already completed in other languages.
So one of the problem was:-
Find the sum of the elements in Array
Arr = [1,2,[7,8,3],1,[3,[2,[4,5]],6]]
I have tried making an interface type array but that also didn't work
func ProdSum(prods interface{},sum int ,depth int){
for id,val:=range prods{
if isArray(val){
ProdSum(val,sum,depth)
}
}
}
type arr []interface{}
func main(){
arra:=arr{5,2,arr{7,-1},3,arr{6,arr{-13,8},4}}
ProdSum(arra,0,1)
}
Error :
./prodsum.go:16:14: cannot range over prods (type interface {})
Use a type assertion or type switch to determine if a value is a slice or an integer. Here's how to do it using a type switch:
func sum(value interface{}) int {
switch value := value.(type) {
case arr:
var result int
for _, v := range value {
result += sum(v)
}
return result
case int:
return value
default:
panic("type not handled")
}
}
Call it like this:
a := arr{5, 2, arr{7, -1}, 3, arr{6, arr{-13, 8}, 4}}
n := sum(a)
Run it on the playground.
The way you are declaring and initializing the array is correct, although you don't need the arr type.
Without the arr type it will look like this:
Running in Go Playground
myArr := []interface{}{1, 2, []int{7, 8, 3}, 1, []interface{}{3, []interface{}{2, []int{4, 5}}, 6}}
Now, the specific error you are seeing is not related to your slice declaration. It is related to the function signature of ProdSum.
ProdSum takes an argument of type interface{}- which is not a slice so you cannot iterate over it. You want the argument to have a type of []interface{} (slice of empty interface):
func ProdSum(prods []interface{}, sum int, depth int) {
for itr, prod := range prods {
// prod has type: interface{}
}
}
Keep in mind that you will need to do type assertions when reading the values out of the slice and summing them. You can't add an interface{} to anything because (before an assertion to int) Go doesn't recognize it as a number.

How to check variable type is map in Go language

map1 := map[string]string{"name":"John","desc":"Golang"}
map2 := map[string]int{"apple":23,"tomato":13}
so,How to check variable type is map in Go language?
You can use the reflect.ValueOf() function to get the Value of those maps, and then get the Kind from the Value, which has a Map entry (reflect.Map).
http://play.golang.org/p/5AUKxECqNA
http://golang.org/pkg/reflect/#Kind
Here's a more specific example that does the comparison with reflect.Map:
http://play.golang.org/p/-qr2l_6TDq
package main
import (
"fmt"
"reflect"
)
func main() {
map1 := map[string]string{"name": "John", "desc": "Golang"}
map2 := map[string]int{"apple": 23, "tomato": 13}
slice1 := []int{1,2,3}
fmt.Printf("%v is a map? %v\n", map1, reflect.ValueOf(map1).Kind() == reflect.Map)
fmt.Printf("%v is a map? %v\n", map2, reflect.ValueOf(map2).Kind() == reflect.Map)
fmt.Printf("%v is a map? %v\n", slice1, reflect.ValueOf(slice1).Kind() == reflect.Map)
}
prints:
map[name:John desc:Golang] is a map? true
map[apple:23 tomato:13] is a map? true
[1 2 3] is a map? false
If you want to know the more specific map type, you can use reflect.TypeOf():
http://play.golang.org/p/mhjAAdgrG4
You can use fmt and avoid using reflection.
func isMap(x interface{}) bool {
t := fmt.Sprintf("%T", x)
return strings.HasPrefix(t, "map[")
}
map1 := map[string]string{"name": "John", "desc": "Golang"}
fmt.Printf("map1 is a map? %t", isMap(map1))
Produces:
map1 is a map? true
https://play.golang.org/p/OVm-_jgz33r
Reflection is discouraged in Go. To quote Rob Pike,
"Clear is better than clever" and "Reflection is never clear."

How to remove an item from a slice by calling a method on the slice

Go has stumped me again. Hopefully someone can help. I've created a slice (mySlice) that contains pointers to structs (myStruct).
The problem is the "Remove" method. When we're inside "Remove" everything is fine, but once we return, the slice size hasn't changed, and so we see the last element listed twice.
I originally tried writing "Remove" using the same pattern used in the "Add" method, but it wouldn't compile and has been commented out.
I can get it to work by returning the newly created slice to the calling function, but I don't want to do this because mySlice (ms) is a singleton.
And if I hadn't asked enough already...
The code for the "Add" method is working, although I'm not sure how. From what I can gather "Add" is receiving a pointer to the slice header (the 3 item "struct"). From what I've read, the length and capacity of an slice don't get passed to methods (when passing by value), so perhaps passing a pointer to the slice allows the method to see and use the length and capacity thereby allowing us to "append". If this is true, then why doesn't the same pattern work in "Remove"?
Thanks very much for everyone's insights and help!
package main
import (
"fmt"
)
type myStruct struct {
a int
}
type mySlice []*myStruct
func (slc *mySlice) Add(str *myStruct) {
*slc = append(*slc, str)
}
//does not compile with reason: cannot slice slc (type *mySlice)
//func (slc *mySlice) Remove1(item int) {
// *slc = append(*slc[:item], *slc[item+1:]...)
//}
func (slc mySlice) Remove(item int) {
slc = append(slc[:item], slc[item+1:]...)
fmt.Printf("Inside Remove = %s\n", slc)
}
func main() {
ms := make(mySlice, 0)
ms.Add(&myStruct{0})
ms.Add(&myStruct{1})
ms.Add(&myStruct{2})
fmt.Printf("Before Remove: Len=%d, Cap=%d, Data=%s\n", len(ms), cap(ms), ms)
ms.Remove(1) //remove element 1 (which also has a value of 1)
fmt.Printf("After Remove: Len=%d, Cap=%d, Data=%s\n", len(ms), cap(ms), ms)
}
and the results...
Before Remove: Len=3, Cap=4, Data=[%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{1}) %!s(*main.myStruct=&{2})]
Inside Remove = [%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{2})]
After Remove: Len=3, Cap=4, Data=[%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{2}) %!s(*main.myStruct=&{2})]
You were right the first time with Remove1(). Remove gets a copy of the slice and therefore cannot change the length of the slice.
The issue in your remove function is that according to order of operations in Go, slicing comes before dereferencing.
The fix is to change *slc = append(*slc[:item], *slc[item+1:]...) to *slc = append((*slc)[:item], (*slc)[item+1:]...).
However I would recommend the following for readability and maintainability:
func (slc *mySlice) Remove1(item int) {
s := *slc
s = append(s[:item], s[item+1:]...)
*slc = s
}
Because append would not necessarily return the same address of reference to the slice, as Stephen Weinberg has pointed out.
Another way to workaround with this limitation is defining a struct that wraps the slice.
for example:
package main
import "fmt"
type IntList struct {
intlist []int
}
func (il *IntList) Pop() {
if len(il.intlist) == 0 { return }
il.intlist = il.intlist[:len(il.intlist)-1]
}
func (il *IntList) Add(i... int) {
il.intlist = append(il.intlist, i...)
}
func (il *IntList) String() string {
return fmt.Sprintf("%#v",il.intlist)
}
func main() {
intlist := &IntList{[]int{1,2,3}}
fmt.Println(intlist)
intlist.Pop()
fmt.Println(intlist)
intlist.Add([]int{4,5,6}...)
fmt.Println(intlist)
}
output:
[]int{1, 2, 3}
[]int{1, 2}
[]int{1, 2, 4, 5, 6}

Resources