Golang slices append - go

I am having a problem when appending to my slice using Golang.
Here is my code:
func MatchBeaconWithXY(w http.ResponseWriter, r *http.Request) ([]types.BeaconDataXY, error) {
context := appengine.NewContext(r)
returnBeaconData := []types.BeaconDataXY{}
beacondata, err := GetBeaconData(w, r)
if err != nil {
log.Errorf(context, "error getting beacondata %v", err)
w.WriteHeader(http.StatusInternalServerError)
return nil, err
}
for index, element := range beacondata {
q := datastore.NewQuery("physicalbeacondata").Filter("NamespaceID =", element.NamespaceID).Filter("InstanceID =", element.InstanceID)
beacondatastatic := []types.BeaconDataStatic{}
_, err := q.GetAll(context, &beacondatastatic)
if err != nil {
log.Errorf(context, "cant get query %v", err)
w.WriteHeader(http.StatusInternalServerError)
return nil, err
}
var beacondataXY = new(types.BeaconDataXY)
beacondataXY.NamespaceID = element.NamespaceID
beacondataXY.InstanceID = element.InstanceID
beacondataXY.XCoord = beacondatastatic[0].XCoord
beacondataXY.YCoord = beacondatastatic[0].YCoord
beacondataXY.Distance = element.Distance
returnBeaconData = append(returnBeaconData, beacondataXY...)
log.Infof(context, "beaondataXY tot %v", beacondataXY)
}
The beacondataxy.go contains this:
package types
type BeaconDataXY struct {
InstanceID string
NamespaceID string
XCoord float64
YCoord float64
Distance float64
}
The error message is this:
utils.go:139: cannot use beacondataXY (type *types.BeaconDataXY) as
type []types.BeaconDataXY in append
I don't really know how to handle slices in Golang, even after reading some tutorials that makes perfect sense. I'm not sure what I'm doing wrong.
I want to have an array/slice with types inside, return BeaconData is of []types. BeaconDataXY and it should contain single types of BeaconDataXY.
Thanks for all help.
EDIT:
The code now looks like this:
func MatchBeaconWithXY(w http.ResponseWriter, r *http.Request) ([]types.BeaconDataXY, error) {
context := appengine.NewContext(r)
//returnBeaconData := []types.BeaconDataXY{}
returnBeaconData := make([]types.BeaconDataXY, 1)
beacondata, err := GetBeaconData(w, r)
if err != nil {
log.Errorf(context, "error getting beacondata %v", err)
w.WriteHeader(http.StatusInternalServerError)
return nil, err
}
for _, element := range beacondata {
q := datastore.NewQuery("physicalbeacondata").Filter("NamespaceID =", element.NamespaceID).Filter("InstanceID =", element.InstanceID)
beacondatastatic := []types.BeaconDataStatic{}
_, err := q.GetAll(context, &beacondatastatic)
if err != nil {
log.Errorf(context, "cant get query %v", err)
w.WriteHeader(http.StatusInternalServerError)
return nil, err
}
var beacondataXY = types.BeaconDataXY{}
beacondataXY.NamespaceID = element.NamespaceID
beacondataXY.InstanceID = element.InstanceID
beacondataXY.XCoord = beacondatastatic[0].XCoord
beacondataXY.YCoord = beacondatastatic[0].YCoord
beacondataXY.Distance = element.Distance
returnBeaconData = append(returnBeaconData, beacondataXY)
//log.Infof(context, "beaondataXY tot %v", beacondataXY)
}

With this assignment:
var beacondataXY = new(types.BeaconDataXY)
you are creating a variable of type *types.BeaconDataXY. Just create a new BeaconDataXY like this:
var beacondataXY = types.BeaconDataXY{}
When appending to your array do it like this:
returnBeaconData = append(returnBeaconData, beacondataXY)
The "..." would assume that beacondataXY is an array but it isn't, you just want to append beacondataXY to returnBeaconData. See https://golang.org/ref/spec#Appending_and_copying_slices for an explanation of what "..." means in this context.

Try returnBeaconData = append(returnBeaconData, *beacondataXY)
new() built-in function returns a pointer, you can alternatively write:
var beacondataXY = types.BeaconDataXY{}

Related

How can I save and retrieve a map into redis using redigo?

I have a map like this, which I want to save/retrive from redis using redigo:
animals := map[string]bool{
"cat": true,
"dog": false,
"fox": true,
}
The length of the map may vary.
I tried these function:
func SetHash(key string, value map[string]bool) error {
conn := Pool.Get()
defer conn.Close()
_, err := conn.Do("HMSET", key, value)
if err != nil {
return fmt.Errorf("error setting key %s to %s: %v", key, value, err)
}
return err
}
func GetHash(key string) (map[string]bool, error) {
conn := Pool.Get()
defer conn.Close()
val, err := conn.Do("HGETALL", key)
if err != nil {
fmt.Errorf("error setting key %s to %s: %v", key, nil, err)
return nil, err
}
return val, err
}
But can not make GetHash correctly. I've checked the docs examples and it was not helpful. So appreciate your help to have a working example.
HMSET is deprecated, use HSET instead, no effect here though.
The map[string]bool may be flattened with AddFlat() for SetHash().
c.Do("HSET", redis.Args{}.Add("key").AddFlat(value)...)
For GetHash(), use Values(). You may use ScanStruct() to map to a struct or loop through the values to create a map dynamically.
v, err := redis.Values(c.Do("HGETALL", key))
redis.ScanStruct(v, &myStruct);
See example from redigo tests in scan_test.go.
The application is responsible for converting structured types to and from the types understood by Redis.
Flatten the map into a list of arguments:
func SetHash(key string, value map[string]bool) error {
conn := Pool.Get()
defer conn.Close()
// Create arguments: key field value [field value]...
var args = []interface{}{key}
for k, v := range value {
args = append(args, k, v)
}
_, err := conn.Do("HMSET", args...)
if err != nil {
return fmt.Errorf("error setting key %s to %v: %v", key, value, err)
}
return err
}
Convert the returned field value pairs to a map:
func GetHash(key string) (map[string]bool, error) {
conn := Pool.Get()
defer conn.Close()
values, err := redis.Strings(conn.Do("HGETALL", key))
if err != nil {
return nil, err
}
// Loop through [field value]... and parse value as bool.
m := map[string]bool{}
for i := 0; i < len(values); i += 2 {
b, err := strconv.ParseBool(value)
if err != nil {
return nil, errors.New("value not a bool")
}
m[key] = b
}
return m, nil
}

Getting data from Firestore to display a go template file

How can I get the p.template from firestore, which is a string, into template.ParseFiles function? Is it possible to use the field value in the function to select the correct template file?
type Property struct {
Name string `firestore:"name"`
ApprovedOrigins []interface{} `firestore:"approvedOrigins"`
Template string `firestore:"selected"`
}
As you can see above the firestore field name is selected
func serveHandler(w http.ResponseWriter, r *http.Request, params map[string]string) {
ctx := context.Background()
client, err := firestore.NewClient(ctx, projectId)
if err != nil {
// TODO: Handle error.
log.Println("FIREBASE ERROR:", err)
}
// collection group query in firestore
q := client.CollectionGroup("data").Where("approvedOrigins", "array-contains", r.Host).Limit(1)
// iterate through the document query
iter := q.Documents(ctx)
defer iter.Stop()
for {
doc, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
// TODO: Handle error.
log.Println("FIREBASE ERROR:", err)
}
fmt.Println("Database connected...")
var p Property
if err := doc.DataTo(&p); err != nil {
fmt.Println(err)
}
fmt.Println(p.Template) // This is not logging any data/string
t, _ := template.ParseFiles(p.Template + ".html")
fmt.Println(t.Execute(w, p)) //504 error happens here
}
}

How to append objects to a slice?

I am new to golang and I'd like to aggregaet query results into a results slice to be pushed to the browser. Here is the code:
type Category struct {
Id bson.ObjectId `bson:"_id,omitempty"`
Name string
Description string
Tasks []Task
}
type Cats struct {
category Category
}
func CategoriesCtrl(w http.ResponseWriter, req *http.Request) {
session, err := mgo.Dial("localhost")
if err != nil {
panic(err)
}
defer session.Close()
session.SetMode(mgo.Monotonic, true)
c := session.DB("taskdb").C("categories")
iter := c.Find(nil).Iter()
result := Category{}
results := []Cats //Here is the problem
for iter.Next(&result) {
results = append(results, result)
fmt.Printf("Category:%s, Description:%s\n", result.Name, result.Description)
tasks := result.Tasks
for _, v := range tasks {
fmt.Printf("Task:%s Due:%v\n", v.Description, v.Due)
}
}
if err = iter.Close(); err != nil {
log.Fatal(err)
}
fmt.Fprint(w, results)
}
But instead I get
type []Cats is not an expression
How can I fix this?
You can say
results := make([]Cats, 0)
or
var results []Cats
or
results := []Cats{}
instead.
You can use results := make([]Cats, len) instead, where len is the initial length of slice.
results := []Cats{} will also work.
If you use var results []Cats, its initial value is nil so you'd need to initialize it before using append.

golang unencode string to be parsable into json

How do I get this to work?
s, _ := url.QueryUnescape("%22%7B%5C%22sessionId%5C%22%3A%5C%225331b937-7b55-4c2d-798a-25e574a7e8af%5C%22%2C%5C%22userId%5C%22%3A2%2C%5C%22userName%5C%22%3A%5C%22datami_op%5C%22%2C%5C%22userEmail%5C%22%3A%5C%22datami_op%40example.com%5C%22%2C%5C%22userRoles%5C%22%3A%5B%5C%22operator%5C%22%5D%7D%22")
fmt.Println(s)
//s := "{\"sessionId\":\"5331b937-7b55-4c2d-798a-25e574a7e8af\",\"userId\":2,\"userName\":\"op\",\"userEmail\":\"datami_op#example.com\",\"userRoles\":[\"operator\"]}"
var i Info
err := json.Unmarshal([]byte(s), &i)
fmt.Println(i, err)
You can either manually remove the quoting yourself, as you have in your comment, or you could unmarshal first as a json string:
var unquote string
err := json.Unmarshal([]byte(s), &unquote)
fmt.Println(unquote, err)
var i Info
err = json.Unmarshal([]byte(unquote), &i)
fmt.Println(i, err)
I believe this does what you want:
GoPlay
Essentially you implement unmarshalJsonJson (clever name, I know)...
The function will unmarshal as a json string, then use that string in the Info unmarshalling.
func unmarshalJsonJson(inval string) (*Info, error) {
var s string
err := json.Unmarshal([]byte(inval), &s)
if err != nil {
return nil, err
}
info := new(Info)
err = json.Unmarshal([]byte(s), info)
if err != nil {
return nil, err
}
return info, nil
}
OUTPUT
main.Info{
SessionId:"5331b937-7b55-4c2d-798a-25e574a7e8af",
UserId:2,
Username:"datami_op",
Email:"datami_op#example.com",
Roles:[]string{"operator"},
}

pass interface pointer and assignment value

I want to write a file cache in Go. I am using gob encoding, and saving to a file, but my get function has some problem:
package main
import (
"encoding/gob"
"fmt"
"os"
)
var (
file = "tmp.txt"
)
type Data struct {
Expire int64
D interface{}
}
type User struct {
Id int
Name string
}
func main() {
user := User{
Id: 1,
Name: "lei",
}
err := set(file, user, 10)
if err != nil {
fmt.Println(err)
return
}
user = User{}
err = get(file, &user)
if err != nil {
fmt.Println(err)
return
}
//user not change.
fmt.Println(user)
}
func set(file string, v interface{}, expire int64) error {
f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
if err != nil {
return err
}
defer f.Close()
//wrapper data
//save v in data.D
data := Data{
Expire: expire,
D: v,
}
gob.Register(v)
enc := gob.NewEncoder(f)
err = enc.Encode(data)
if err != nil {
return err
}
return nil
}
func get(file string, v interface{}) error {
f, err := os.OpenFile(file, os.O_RDONLY, 0600)
if err != nil {
return err
}
defer f.Close()
var data Data
dec := gob.NewDecoder(f)
err = dec.Decode(&data)
if err != nil {
return err
}
//get v
v = data.D
fmt.Println(v)
return nil
}
The get function passes interface type and I want to change the value, but not change.
http://play.golang.org/p/wV7rBH028o
In order to insert an unknown value into v of type interface{}, you need to use reflection. This is somewhat involved, but if you want to support this in full, you can see how its done by walking through the decoding process in some of the encoding packages (json, gob).
To get you started, here's a basic version of your get function using reflection. This skips a number of checks, and will only decode something that was encoded as a pointer.
func get(file string, v interface{}) error {
f, err := os.OpenFile(file, os.O_RDONLY, 0600)
if err != nil {
return err
}
defer f.Close()
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr || rv.IsNil() {
panic("need a non nil pointer")
}
var data Data
dec := gob.NewDecoder(f)
err = dec.Decode(&data)
if err != nil {
return err
}
dv := reflect.ValueOf(data.D)
if dv.Kind() != reflect.Ptr {
panic("didn't decode a pointer")
}
rv.Elem().Set(dv.Elem())
return nil
}
I would actually suggest an easier way to handle this in your own code, which is to have the Get function return an interface{}. Since you will know what the possible types are at that point, you can use a type switch to assert the correct value.
An alternative approach is to return directly the value from the file:
func get(file string) (interface{}, error) {
f, err := os.OpenFile(file, os.O_RDONLY, 0600)
if err != nil {
return nil, err
}
defer f.Close()
var data Data
dec := gob.NewDecoder(f)
err = dec.Decode(&data)
if err != nil {
return nil,err
}
fmt.Println(data.D)
return data.D,nil
}
full working example: http://play.golang.org/p/178U_LVC5y

Resources