i give a dataset from mysql-database to go-template. the result have multiple rows but all values is one string!?
type Tasks struct {
tid int
pid int
uid int
del int
finisch int
open int
inprocess int
abnahme int
fertig int
finischdatum string
erstellt string
start string
ende string
name string
beschreibung string
}
type Daten struct {
Tabledata []*Tasks
}
d := Daten{}
rows, err := db.Query("SELECT * FROM tasks WHERE pid=? AND del=0", pid)
checkError(err)
defer rows.Close()
rs := make([]*Tasks, 0)
for rows.Next() {
rst := new(Tasks)
err := rows.Scan(&rst.tid, &rst.pid, &rst.uid, &rst.del, &rst.finisch, &rst.open, &rst.inprocess, &rst.abnahme, &rst.fertig, &rst.finischdatum, &rst.erstellt, &rst.start, &rst.ende, &rst.name, &rst.beschreibung)
if err != nil {
log.Println(err)
}
rs = append(rs, rst)
}
d.Tabledata = rs
template:
{{ range $key, $values := .Tabledata }}
<li><strong>{{ $key }}</strong>: </li>
{{range $values}}
{{.}}
{{end}}
{{ end }}
When I look in the first range and give me the $values as one string and the second range is death.
What's my Problem?
In your code, since you didn't tell, I guess the variable 'd' is passed to the template. 'd' is of type Daten and thus contain a field named TableData. This field is a slice of *Tasks. This is why in your template you can loop on it using range.
The range keyword returns two values, the first one, '$key' in your code, is the index of the slice, and the second one, '$values' in your code is the value pointed by TableData[$key]. Because TableData is a slice of type *Tasks, that means $values is of type *Tasks. So not an array nor a slice.
That means, you can't loop on $values because it's not a slice. But, you can access all the exported fields of Tasks. So depending on what you want to display in your template, you can do the following:
// Inside the firts range
<li><strong>{{ $key }}</strong>: </li>
name: $values.Name
open: $values.Open
Note the uppercase on the field's name, in order to access a field it shall be exported, and thus starts with an upper case in the type declaration.
Related
In the Params model I have an array of int Cat_id
I make a request: localhost:8080/products/?cat_id=1,2
And I want to display multiple products from these two categories. How can I parsely build my query?
My func:
func GetAllIproducts(q *models.Products, pagination *models.Params) (*[]models.Products, error) {
var prod []models.Products
offset := (pagination.Page - 1) * pagination.Limit
result := config.DB.Model(&models.Products{}).Where(q).Where("cat_id=?", pagination.Cat_id).Limit(pagination.Limit).Offset(offset).Find(&prod) //Problem is here
if result.Error != nil {
msg := result.Error
return nil, msg
}
return &prod, nil
}
When i use Debug i got this:
SELECT * FROM "products" WHERE cat_id=(1,2) AND "products"."deleted_at" IS NULL
Assuming that the cat_id is an integer (lets assume int64), you could these two things:
Convert pagination.Cat_id string to an []int64 slice (lets call this variable catIDs of type []int64) to get a slice with separated int64 elements.
Change your Where clause to something like this:
result := config.DB.Model(&models.Products{}).Where(q).Where("cat_id IN (?)", catIDs).Limit(pagination.Limit).Offset(offset).Find(&prod)
I need to take applicant's first name, second name and GPA, then output only the first N applicants. For example, I have 5 applicants, but only N=3 can pass through.
To do this task, I decided to use a slice of struct.
The struct looks like this:
type Applicant struct {
firstName string
secondName string
GPA float64
}
I created a slice and initialized it:
applicants := []Applicant{}
...
fmt.Scan(&firstName, &lastName, &GPA)
applicants = append(applicants, Applicant{firstName, lastName, GPA})
Now my task is to output only names of first 3 applicants with highest GPA. I already sorted the slice from the best GPA to the worst.
I tried to do output applicants slice like this, but got error:
for _, applicant := range applicants {
fmt.Println(applicant.secondName + " " + applicant.secondName)
}
Can you help me with slice name output?
To get the first 3 with highest GPA you first sort the slice (what you alread did) and then just create a subslice:
func GetTopThree(applicants []Applicant) []Applicant {
sort.Slice(applicants, func(i, j int) bool {
return applicants[i].GPA > applicants[j].GPA
})
return applicants[:3]
}
To just get the names you can create a new slice
func GetTopThreeNames(applicants []Applicant) []string {
var topThree []string
for i := 0; i < int(math.Min(3, float64(len(applicants)))); i++ {
topThree = append(topThree, applicants[i].firstName)
}
return topThree
}
If you want to map the first names and last names separately, this could be an approach:
func TopThreeNames(applicants []Applicant) [][2]string {
top := applicants[:int(math.Min(3, float64(len(applicants))))]
var names [][2]string
for _, a := range top {
names = append(names, [2]string{a.firstName, a.secondName})
}
return names
}
The function maps each Applicant element to an array of length two, whereby the first element is equal to its first name and the second element to its second name.
For instance (unsafe since the length of the slice could be empty):
names := TopThreeNames(applicants)
first := names[0]
fmt.Printf("First name: %s and last name: %s\n", first[0], first[1])
If your task really is just to print out the names then this is one possible way
for i := 0; i < 3 && i < len(applicants); i++ {
fmt.Printf("%s %s\n", applicants[i].firstName, applicants[i].secondName)
}
Note that the list must be sorted first like is is shown in other posts.
This question already has answers here:
Access struct property by name
(5 answers)
Golang dynamic access to a struct property
(2 answers)
How to access to a struct parameter value from a variable in Golang
(1 answer)
Closed 9 months ago.
Came from javascript background, and just started with Golang. I am learning all the new terms in Golang, and creating new question because I cannot find the answer I need (probably due to lack of knowledge of terms to search for)
I created a custom type, created an array of types, and I want to create a function where I can retrieve all the values of a specific key, and return an array of all the values (brands in this example)
type Car struct {
brand string
units int
}
....
var cars []Car
var singleCar Car
//So i have a loop here and inside the for-loop, i create many single cars
singleCar = Car {
brand: "Mercedes",
units: 20
}
//and i append the singleCar into cars
cars = append(cars, singleCar)
Now what I want to do is to create a function that I can retrieve all the brands, and I tried doing the following. I intend to have key as a dynamic value, so I can search by specific key, e.g. brand, model, capacity etc.
func getUniqueByKey(v []Car, key string) []string {
var combined []string
for i := range v {
combined = append(combined, v[i][key])
//this line returns error -
//invalid operation: cannot index v[i] (map index expression of type Car)compilerNonIndexableOperand
}
return combined
//This is suppose to return ["Mercedes", "Honda", "Ferrari"]
}
The above function is suppose to work if i use getUniqueByKey(cars, "brand") where in this example, brand is the key. But I do not know the syntaxes so it's returning error.
Seems like you're trying to get a property using a slice accessor, which doesn't work in Go. You'd need to write a function for each property. Here's an example with the brands:
func getUniqueBrands(v []Car) []string {
var combined []string
tempMap := make(map[string]bool)
for _, c := range v {
if _, p := tempMap[c.brand]; !p {
tempMap[c.brand] = true
combined = append(combined, c.brand)
}
}
return combined
}
Also, note the for loop being used to get the value of Car here. Go's range can be used to iterate over just indices or both indices and values. The index is discarded by assigning to _.
I would recommend re-using this code with an added switch-case block to get the result you want. If you need to return multiple types, use interface{} and type assertion.
Maybe you could marshal your struct into json data then convert it to a map. Example code:
package main
import (
"encoding/json"
"fmt"
)
type RandomStruct struct {
FieldA string
FieldB int
FieldC string
RandomFieldD bool
RandomFieldE interface{}
}
func main() {
fieldName := "FieldC"
randomStruct := RandomStruct{
FieldA: "a",
FieldB: 5,
FieldC: "c",
RandomFieldD: false,
RandomFieldE: map[string]string{"innerFieldA": "??"},
}
randomStructs := make([]RandomStruct, 0)
randomStructs = append(randomStructs, randomStruct, randomStruct, randomStruct)
res := FetchRandomFieldAndConcat(randomStructs, fieldName)
fmt.Println(res)
}
func FetchRandomFieldAndConcat(randomStructs []RandomStruct, fieldName string) []interface{} {
res := make([]interface{}, 0)
for _, randomStruct := range randomStructs {
jsonData, _ := json.Marshal(randomStruct)
jsonMap := make(map[string]interface{})
err := json.Unmarshal(jsonData, &jsonMap)
if err != nil {
fmt.Println(err)
// panic(err)
}
value, exists := jsonMap[fieldName]
if exists {
res = append(res, value)
}
}
return res
}
How to append to empty interface (that has been verified to be a *[]struct)?
func main() {
var mySlice []myStruct // myStruct can be any struct (dynamic)
decode(&mySlice, "...")
}
func decode(dest interface{}, src string) {
// assume dest has been verified to be *[]struct
var modelType reflect.Type = getStructType(dest)
rows, fields := getRows(src)
for _, row := range rows {
// create new struct of type modelType and assign all fields
model := reflect.New(modelType)
for field := fields {
fieldValue := getRowValue(row, field)
model.Elem().FieldByName(field).Set(fieldValue)
}
castedModelRow := model.Elem().Interface()
// append model to dest; how to do this?
// dest = append(dest, castedModelRow)
}
}
Things I've tried:
This simply panics: reflect: call of reflect.Append on ptr Value (as we pass &mySlice instead of mySlice)
dest = reflect.Append(reflect.ValueOf(dest), reflect.ValueOf(castedModelRow))
This works but doesn't set the value back to dest... in main func, len(mySlice) remains 0 after decode function is called.
func decode(dest interface{}, src string) {
...
result := reflect.MakeSlice(reflect.SliceOf(modelType), rowCount, rowCount)
for _, row : range rows {
...
result = reflect.Append(result, reflect.ValueOf(castedModelRow))
}
dest = reflect.ValueOf(result)
}
Here's how to fix the second decode function shown in the question. The statement
dest = reflect.ValueOf(result)
modifies local variable dest, not the caller's value. Use the following statement to modify the caller's slice:
reflect.ValueOf(dest).Elem().Set(result)
The code in the question appends decoded elements after the elements created in reflect.MakeSlice. The resulting slice has len(rows) zero values followed by len(rows) decoded values. Fix by changing
result = reflect.Append(result, reflect.ValueOf(castedModelRow))
to:
result.Index(i).Set(model)
Here's the update version of the second decode function in the question:
func decode(dest interface{}, src string) {
var modelType reflect.Type = getStructType(dest)
rows, fields := getRows(src)
result := reflect.MakeSlice(reflect.SliceOf(modelType), len(rows), len(rows))
for i, row := range rows {
model := reflect.New(modelType).Elem()
for _, field := range fields {
fieldValue := getRowValue(row, field)
model.FieldByName(field).Set(fieldValue)
}
result.Index(i).Set(model)
}
reflect.ValueOf(dest).Elem().Set(result)
}
Run it on the Playground.
You were very close with your original solution. You had to de-reference the pointer before calling the append operation. This solution would be helpful if your dest already had some existing elements and you don't want to lose them by creating a newSlice.
tempDest := reflect.ValueOf(dest).Elem()
tempDest = reflect.Append(tempDest, reflect.ValueOf(model.Interface()))
Similar to how #I Love Reflection pointed out, you finally need to set the new slice back to the pointer.
reflect.ValueOf(dest).Elem().Set(tempDest)
Overall Decode:
var modelType reflect.Type = getStructType(dest)
rows, fields := getRows(src)
tempDest := reflect.ValueOf(dest).Elem()
for _, row := range rows {
model := reflect.New(modelType).Elem()
for _, field := range fields {
fieldValue := getRowValue(row, field)
model.FieldByName(field).Set(fieldValue)
}
tempDest = reflect.Append(tempDest, reflect.ValueOf(model.Interface()))
}
reflect.ValueOf(dest).Elem().Set(tempDest)
I connect BigQuery with Go language as the following API documentation demonstrates,
https://cloud.google.com/bigquery/docs/reference/libraries?hl=en_US
After that, I need to get sql results in specific row and column, and judge if it equals a specific string. Could I change bigquery.Value to string and how to do that?
See how to use RowIterator.Next() here.
Next loads the next row into dst. Its return value is iterator.Done if there are no more results. Once Next returns iterator.Done, all subsequent calls will return iterator.Done.
dst may implement ValueLoader, or may be a *[]Value, *map[string]Value, or struct pointer.
Value is of type interface{} so if you are sure that value you have is string str := fmt.Sprintf("%v", row[i]) should work. It is often better to define a struct type that has members representing fields of query result row (with types mapped according to the table in documentation I linked above) and give the pointer to it to RowIterator.Next() instead of slice/map of bigquery.Value.
type myRow struct {
Name string
Num int
}
// ...
q := client.Query("select name, num from t1")
it, err := q.Read(ctx)
// handle err
for {
// instead of: var row []bigquery.Value
var row myRow // <-- use custom struct type here
err := it.Next(&row)
if err == iterator.Done {
break
}
// handle err != nil
someFuncThatTakesString(row.Name)
}