I have some Go code that is returning a map of values but I only need some of the results. Is there a way I can test/filter the key of the map against a string array (or something similar) to give a simplified result rather than a bunch of if statements? All the samples I have looked up had fixed values to filter against.
A simple example is below, but rather than supplying the string I want to have a list of possible values so I can get a reduced list.
package main
import "fmt"
type colors struct {
animal string
COLOR []string
}
func main() {
// Map animal names to color strings.
colors := map[string]string{
"bird": "blue",
"snake": "green",
"cat": "black",
}
// Display string.
fmt.Println(colors)
}
Yes, you can test/filter a map using range. If you have all the possible values, you can simply compare them to the map using a key/value lookup and make the structs based off of that.
package main
import (
"fmt"
)
type colors struct {
animal string
COLOR []string
}
func main() {
//the list of values
possibleValues := []string{"bird","dog", "cat"}
// Map animal names to color strings.
foo := map[string]string{
"bird": "blue",
"snake": "green",
"cat": "black",
}
//slice of objects of your struct
objects := []colors{}
//for every value in the possible values
for _, v := range possibleValues {
//if it's in the map, make a new struct and append it to the slice of objects
if val,ok := foo[v]; ok {
objects = append(objects, colors{animal:v,COLOR:[]string{val}})
}
}
// Display string.
fmt.Println(objects)
}
https://play.golang.org/p/njD6E_WssHT
Related
I am new to GOlang, I trying to filter slice of struct in GO using struct which contain some parameter for filter it. I am trying to do below things but its not work for me. In that code I have filter function which take slice of struct i.e GRN which need to filter using FilterParameter struct.
My Struct which need to filer
type GRN struct {
DocType string `json:"docType"`
GRNNO string `json:"GRNNO"`
PRID string `json:"PRID"`
PRODUCE string `json:"PRODUCE"`
VARIETY string `json:"VARIETY"`
}
manyGRNUsers := []GRN{{"GRNAsset","GRN0000109","PRFAINAPKR0086","Sweetcorn","CP2"},
{"GRNAsset","GRN0000110","PRFAINAPKR0087","Sweetcorn","CP1",},
{"GRNAsset","GRN0000112","PRFAINAPKR0087", "Sweetcorn","CP2",},
{"GRNAsset","GRN0000113","PRFAINAPKR0089","Apple","Gala",}}
Struct which will used for filter parameter.here one thing the field in FilterParameter is dyanamic as above GRN struct value. it could me more then two field but less than or equal to GRN struct field
type FilterParameter struct {
PRODUCE string `json:"PRODUCE"`
VARIETY string `json:"VARIETY"`
}
manyFilterParameter := []FilterParameter{{"Sweetcorn","CP2" }
I tried below function but its not work for me as I can do only with one field from FilterParameter i.e PRODUCE only and its not dynamic
func filter(fu []GRN, su []FilterParameter) (out []GRN) {
f := make(map[string]struct{}, len(su))
for _, u := range su {
f[u.PRODUCE] = struct{}{}
}
for _, u := range fu {
if _, ok := f[u.PRODUCE]; ok {
out = append(out, u)
}
}
return
}
You can't really do what you're trying to do, as it's not possible to iterate the fields of a struct (maybe with reflection). For another approach, you could use maps instead of structs, or even easier, if you can just explicitly use the fields you care about in the function parameters:
package main
import "fmt"
type GRN struct { DocType, GRNNO, PRID, PRODUCE, VARIETY string }
func filter(in []GRN, produce, variety string) []GRN {
var out []GRN
for _, each := range in {
if each.PRODUCE == produce && each.VARIETY == variety {
out = append(out, each)
}
}
return out
}
func main() {
manyGRNUsers := []GRN{
{"GRNAsset","GRN0000109","PRFAINAPKR0086","Sweetcorn","CP2"},
{"GRNAsset","GRN0000110","PRFAINAPKR0087","Sweetcorn","CP1"},
{"GRNAsset","GRN0000112","PRFAINAPKR0087", "Sweetcorn","CP2"},
{"GRNAsset","GRN0000113","PRFAINAPKR0089","Apple","Gala"},
}
out := filter(manyGRNUsers, "Sweetcorn", "CP2")
fmt.Println(out)
}
What I want to do
Convert {"color": "red", "mrkdwn_in":["text"]} in json to a structure with UnMarshal
Change "color" from 'red' to 'blue'
Change the changed structure to json with Marshal
The original "mrkdwn_in":["text"]} is not well preserved.
It becomes "mrkdwn_in":[{"text":""}] or "mrkdwn_in":"".
I want to keep the "mrkdwn_in" part in its original state.
code
package main
import (
"encoding/json"
"fmt"
)
type TestStruct struct {
Color string `json:"color"`
MrkdwnIn []MrkdwnIn `json:"mrkdwn_in"`
}
type MrkdwnIn struct {
Text string `json:"text"`
}
func main() {
jsonData1 := "{\"color\":\"red\",\"mrkdwn_in\":[\"text\"]}"
var body TestStruct
json.Unmarshal([]byte(jsonData1), &body)
body.Color = "blue"
var jsonData2 []byte
jsonData2, _ = json.Marshal(body)
jsonDataText := string(jsonData2)
fmt.Println(jsonDataText)
}
Execution result of the above code
The error is obvious
MrkdwnIn []MrkdwnIn `json:"mrkdwn_in"`
MrkdwnIn is a array of object
MrkdwnIn []string`json:"mrkdwn_in"`
MrkdwnIn should be a array of string
I saw several questions asking on how to merge unique structs and how to merge identical structs.
But how would I merge structs that have some overlap? and which fields get taken & when?
e.g.:
type structOne struct {
id string `json:id`
date string `json:date`
desc string `json:desc`
}
and
type structTwo struct {
id string `json:id`
date string `json:date`
name string `json:name`
}
how would I merge it such that I get
{
id string `json:id`
date string `json:date`
desc string `json:desc`
name string `json:name`
}
also, what happens if in this case the two id's are the same (assuming a join over id's) but the names are different?
In javascript, doing something like Object.assign(structOne, structTwo).
Go is a strongly typed language, unlike javascript you can't merge two struct into one combined struct because all type are determined at compile-time. You have two solution here :
Using embedded struct:
One great solution is to use embedded struct because you don't have to merge anything anymore.
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
)
// Shared field
type common struct {
ID string `json:id`
Date string `json:date`
}
type merged struct {
// Common field is embedded
common
Name string `json:name`
Desc string `json:desc`
}
func main() {
buf := bytes.Buffer{}
buf.WriteString("{ \"id\": \"1\", \"date\": \"27/07/2020\", \"desc\": \"the decription...\" }")
merged := &merged{}
err := json.Unmarshal(buf.Bytes(), merged)
if err != nil {
log.Fatal(err)
}
// Look how you can easily access field from
// embedded struct
fmt.Println("ID:", merged.ID)
fmt.Println("Date:", merged.Date)
fmt.Println("Name:", merged.Name)
fmt.Println("Desc:", merged.Desc)
// Output:
// ID: 1
// Date: 27/07/2020
// Name:
// Desc: the decription...
}
If you want to read more about struct embedding:
golangbyexample.com
travix.io
Using Maps
Another solution is to use maps but you will loose the benefits of struct and methods. This example is not the simplest but there is some great example in the other responses.
In this example I'm using Mergo. Mergo is library that can merge structs and map. Here it is used for creating maps object in the Map methods but you can totally write your own methods.
package main
import (
"fmt"
"log"
"github.com/imdario/mergo"
)
type tOne struct {
ID string
Date string
Desc string
}
// Map build a map object from the struct tOne
func (t1 tOne) Map() map[string]interface{} {
m := make(map[string]interface{}, 3)
if err := mergo.Map(&m, t1); err != nil {
log.Fatal(err)
}
return m
}
type tTwo struct {
ID string
Date string
Name string
}
// Map build a map object from the struct tTwo
func (t2 tTwo) Map() map[string]interface{} {
m := make(map[string]interface{}, 3)
if err := mergo.Map(&m, t2); err != nil {
log.Fatal(err)
}
return m
}
func main() {
dst := tOne{
ID: "destination",
Date: "26/07/2020",
Desc: "destination object",
}.Map()
src := tTwo{
ID: "src",
Date: "26/07/1010",
Name: "source name",
}.Map()
if err := mergo.Merge(&dst, src); err != nil {
log.Fatal(err)
}
fmt.Printf("Destination:\n%+v", dst)
// Output:
// Destination:
// map[date:26/07/2020 desc:destination object iD:destination name:object name
}
Go structs and JavaScript objects are very different. Go structs do not have dynamic fields.
If you want dynamic key/value sets that you can easily iterate over and merge, and are very JSON friendly, why not a map[string]interface{}?
$ go run t.go
map[a:1 b:4]
map[a:1 b:4 c:3]
$ cat t.go
package main
import(
"fmt"
)
type MyObj map[string]interface{}
func (mo MyObj)Merge(omo MyObj){
for k, v := range omo {
mo[k] = v
}
}
func main() {
a := MyObj{"a": 1, "b": 4}
b := MyObj{"b": 2, "c": 3}
b.Merge(a)
fmt.Printf("%+v\n%+v\n", a, b)
}
you can use github.com/fatih/structs to convert your struct to map. Then iterate over that map and choose which fields need to copy over. I have a snippet of code which illustrates this solution.
func MergeStruct (a structOne,b structTwo) map[string]interface{}{
a1:=structs.Map(a)
b1:=structs.Map(b)
/* values of structTwo over writes values of structOne */
var myMap=make(map[string]interface{})
for val,key:=range(a1){
myMap[key]=val
}
for val,key:=range(b1){
myMap[key]=val
}
return myMap
}
You can use the reflect package to do this. Try iterating through the two structs and then you can either use another struct type to store the values or maybe use a map.
Check out this question to find out how you can iterate over a struct.
Check out this question to find out how to get the name of the fields.
Remember to use exported field names for the reflect package to work.
Here is an example which works.
I have a slice of structs that contains 10 cars like:
type struct car {
engine
window
wheel
}
so the slice cars contains 10 car struct.
I would like to know if a function exist such as:
var engines string[] = cars.Getfield("engine") // engines will contain 10 engines names
There's no library function for this.
You can implement manually using reflect package
Example:
type Cars []Car
func (cars Cars) getFieldString(field string) []string {
var data []string
for _, car := range cars {
r := reflect.ValueOf(car)
f := reflect.Indirect(r).FieldByName(field)
data = append(data, f.String())
}
return data
}
Code in Playground here
There's no generics in Go (not until 2.0 at least), so there's not a lot of helper functions out there.
If you need to use this function often, you can implement it as a method for type engines.
Answering #RodolfoAP question under #Joe-Akanesuvan answer (not enough rep to post as a comment):
Even though generics are a part of Go now, there're no functional programming libs in std, I used 1st one I found on awesome-go, so this is probably isn't production-ready code, but that's generally what it would look like:
package main
import (
"fmt"
fp "github.com/repeale/fp-go"
)
type data struct {
field string
anotherField int
}
func main() {
fmt.Printf(
"result: %+v",
fp.Map(func(d data) string { return d.field })(
[]data{
{
field: "apple",
anotherField: 1,
},
{
field: "orange",
anotherField: 2,
},
{
field: "banana",
anotherField: 3,
},
},
),
)
}
Code output:
result: [apple orange banana]
Code in playground here
I'm new working on golang, In this case I have a map [string] which has a struct.
At this point everything is works.
But I would like to have a map[string] in which I can access at the same time to another map[string] which has it self struct.
This is the code in which I'm trying to work.
type myStruct struct{
atrib1 string
atrib2 string
}
var apiRequest map[string] map[string]myStruct
I would like acces to something like this:
func main() {
apiRequest = make(map[string] map[string]myStruct)
apiKeyTypeRequest["Key"]["MyFirstOption"].atrib1 = "first Value first op"
apiKeyTypeRequest["Key"]["MyFirstOption"].atrib2 = "second Value first op"
apiKeyTypeRequest["Key"]["MysecondtOption"].atrib1 = "first Value second op"
}
An alternative to using a map inside a map, is to have a single map[mapKey] where mapKey is a struct:
type mapKey struct {
Key string
Option string
}
The benefits is that you only have to do a single lookup when searching for a myStruct, and you only have to create a single map.
The downside is in case you need to be able to get that options map[string]myStruct map, since it does not exist. Also, you cannot check if a certain key exists or not, because keys and options exists in pairs.
Working example:
package main
import "fmt"
type myStruct struct {
atrib1 string
atrib2 string
}
type mapKey struct {
Key string
Option string
}
func main() {
apiKeyTypeRequest := make(map[mapKey]myStruct)
apiKeyTypeRequest[mapKey{"Key", "MyFirstOption"}] = myStruct{"first Value first op", "second Value first op"}
apiKeyTypeRequest[mapKey{"Key", "MysecondtOption"}] = myStruct{atrib1: "first Value second op"}
fmt.Printf("%+v\n", apiKeyTypeRequest)
}
Playground: http://play.golang.org/p/tGd7ja7QI2
To expand upon previous answers, each map must be declared and instantiated (as well as the struct at the end of the map), that means you'll need to instantiate the "outer" map
mapOfMaps := make(map[string]map[string]myStruct)
as well as the "inner" map(s) for each key you have.
mapOfMaps[key] = make(map[string]myStruct)
The obvious issue you run into here is how do you dynamically check to see if the mapOfMaps[key] has already been instantiated? You do this using the following syntax:
if _, ok := mapOfMaps[key]; !ok {
mapOfMaps[key] = make(map[string]myStruct)
}
This syntax checks to see if mapOfMaps already has an instantiated value for that key, and if not instantiates it. Similarly, you can use the same syntax to instantiate the structs you are using by checking to see of mapOfMaps[key][key2] has been instantiated yet.
if _, ok := mapOfMaps[key][key2]; !ok {
mapOfMaps[key][key2] = new(myStruct)
}
Now you can access the struct through the call to 2 keys and an attribute of the struct
fmt.Println(mapOfMaps[key][key2].atrib1)
As #FUZxxl has said, you need to initialize the sub-map for each outer map, but there is a shorthand syntax for it:
type myStruct struct {
atrib1 string
atrib2 string
}
func main() {
var myMap = map[string]map[string]myStruct{
"foo": {
"bar": {attrib1: "a", attrib2: "b"},
"baz": {"c", "d"}, //or by order...
},
"bar": {
"gaz": {"e", "f"},
"faz": {"g", "h"},
},
}
fmt.Println(myMap["foo"]["bar"].atrib1)
fmt.Println(myMap["bar"]["gaz"].atrib2)
}