My question is about appending to a map which is a filed/key of an interface. This is required to build a JSON object. I am using map[string]interface{} to be able to append any struct types to it. I am not sure if that is a right way as I am still learning Go but I could not find a better way. Below is a link to a playground:
https://play.golang.org/p/cxpSep8OQD.
I think I need to use type accretion but I do not understand how. Any help would be useful.
If all you have are Group values, then declare outJson as a map of *Group:
outJson := make(map[string]*Group, len(groups))
Playground Example
If the map can contain different value types, then use a type assertion to access the group:
g, ok := outJson["a"].(*Group)
if !ok {
panic("handle error a better way")
}
g.Hosts = append(g.Hosts, "hostname")
Playground Example
Related
I'm new to golang, and I'm struggling to see how I can get some generic behavior on a method.
func (s *service) getCars(filter Filter) ([]car, error){
var err error
var cars []car
switch filter {
case filter.Toyota:
cars, err = s.dbClient.getToyotas()
case filter.Honda:
cars, err = s.dbClient.getHondas()
}
return cars, nil
}
The objects returned from s.dbClient.getToyotas() and s.dbClient.getHondas() are data structs with the same fields, but have different types. I should note these returned structs are auto generated, so I don't have the ability to alter them.
It looks like go interfaces only allow methods, and not data fields, so I don't if it's even possible to define a car type that can be used as the return type. I looked at generics as well, but it seems that generics are not allowed on struct methods.
What's the idiomatic way of doing something like this in go?
Defining a common interface is the idiomatic way to go.
Access to fields can be "modeled" by defining accessor methods on interface and then implementing those on specific structs.
In fact, it is a better solution as those methods can do more than simple access to internal, private variables. For example, they can have lazy initialization or cached access built in. Each struct can have unique implementation or you can have a base struct with default one.
I understand how to use multiple return values in go. I further understand that in most cases one of the returns is an error, so ignoring returned values can be dangerous.
Is there a way to ignore a value in struct initializer like this? The below example does not work as Split returns two values, but I am interested only in the first one. I can of course create a variable but...
someFile := "test/filename.ext"
contrivedStruct := []struct{
parentDir string
}{
{ parentDir: filepath.Split(someFile) },
}
It's not possible to use only one of the return values when initializing members in Go.
Using variables clearly expresses your intent.
Go sometimes feels like it could be more succinct, but the Go authors favoured readability over brevity.
Alternatively, use a wrapper function. There are several 'Must' wrapper functions in the standard library, like: template.Must.
func first(args ...string) string {
return args[0]
}
For your particular example, splitting paths, see filepath.Base or filepath.Dir.
No, there is no way to skip one of the returned values in structure initializer.
I am looking for a way to store multiple values for each key (just like we can in python using dictionary) using Go.
Is there a way this can be achieved in Go?
Based on your response in comments I would suggest something like the following using a struct (though if you are only interested in a single value like name for each item in your slice then you could just use a map[int][]string{}
type Thing struct {
name string
age int
}
myMap := map[int][]Thing{}
If you want to add things then you just do...
myMap[100] = append(myMap[100], Thing{"sberry": 37})
Or if you want to create it in place:
myMap := map[int][]Thing{
100: []Thing{Thing{"sberry", 37}},
2: []Thing{Thing{"johndoe", 22}},
}
EDIT: Adding a "nameIn" function based on comments as demo:
func nameIn(things []Thing, name string) bool {
for _, thing := range things {
if thing.name == name {
return true
}
}
return false
}
if nameIn(myMap[100], "John") {
...
If the slices are really big and speed is concern then you could keep a reverse lookup map like map[string]int where an entry would be John: 100, but you will need to most likely use some user defined functions to do your map modifications so it can change the reverse lookup as well. This also limits you by requiring unique names.
Most likely the nameIn would work just fine.
In go, the key/value collection is called a map. You create it with myMap := map[keyType]valType{}
Usually something like mapA := map[string]int{}. If you want to store multiple values per key, perhaps something like:
mapB := map[string][]string{} where each element is itself a slice of strings. You can then add members with something like:
mapB["foo"] = append(mapB["foo"], "fizzbuzz")
For a great read see Go maps in action
I have two golang functions, doing exactly same thing, one takes input a slice and the other takes a map as input. I want to combine this into one function, to avoid duplication.
func DoSomething([]data) {
//do something.
}
func DoSomething(map[string]data) {
//do something.
}
Combined function may look like:
func DoSomethingNew (param type) {
//param could be map or slice
}
I want to know if it is possible to pass different types to same function in golang and how. I googled but couldn't find anything relevant.
You can use interfaces Go Playground
func F(data interface{}) {
switch data.(type){
case []int:
fmt.Println("Slice")
fmt.Println(data)
case map[string]int:
fmt.Println("Map")
fmt.Println(data)
}
fmt.Println()
}
where you actually check for the type and do something based on the type.
There are several ways you could do this, but the simple way would be to make it so DoSomethingNew accepts the interface{} type. Inside of the method you would then do a type switch or in this case with only two options, perhaps just one type assertion, followed by the other, returning error if both fail. Another option would be to have both as arguments and check for nil inside the function with a similar if, else-if, else pattern to handle the error if the input is of neither types you're looking for. To make your code more safe you could move to a more strict interface than the empty one which all types implement. You could also do some method chaining or even implement the method with using the types themselves as the receiving type. Here's an example that shows a few of the ideas; https://play.golang.org/p/_v2AyFjGzv
I want to encode pointers differently than from values. Currently, if we have a struct:
type Order struct {
Item Tool
AssociatedItem *Tool
}
both get inlined into a Order document inside mongo when marshalling.
I need to be able to perform my own serialization in the case of a *Tool. For instance, I could in this case only store the Id for the Too instead of the whole content. Unfortunately the overriding mechanism in mgo is to define a SetBSON() GetBSON for the Tool but it doesn't differentiate between pointers and non pointers.
What would be the best way to handle this?
Use a different type for "pointers", for example:
type SelectiveTool Tool
func (st *SelectiveTool) SetBSON(raw bson.Raw) error {
return raw.Unmarshal(s)
}
type Order struct {
Item Tool
AssociatedItem *SelectiveTool
}