I have this struct definition :
// Two columns, both strings.
type ExampleStructItem struct {
Firstname string
Surname string
}
and I have this slice of column names :
columns := []string{"Firstname", "Surname"}
and I am essentially trying to loop through my slice of column names, and then perform reflection on the corresponding struct to get information about the properties, such as their "Kind" etc.
Just use Type.FieldByName()
var ex ExampleStructItem
t := reflect.TypeOf(ex)
for _, name := range columns {
field, ok := t.FieldByName(name)
if ok {
k := field.Type.Kind()
} else {
// error handling
}
}
Playground
Related
So I have this table Schema that looks like the following
CREATE TABLE artists (
id SERIAL PRIMARY KEY,
foo_user_id TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
time_span TIME_SPAN NOT NULL,
items artist [] NOT NULL
);
In addition, I also have the custom type created in PostgreSQL that is defined as follows
CREATE TYPE artist AS (
artist_name TEXT,
foo_artist_id TEXT,
artist_image_url TEXT,
artist_rank int
);
I am trying to query all rows that have the "foo_user_id" equal to what I pass into the function. Here is the sample code.
func GetHistoricalTopArtists(foo_user_id string) ([]TopArtists, error) {
// connect to DB etc..
// create prepared statement
stmtStr := `SELECT * FROM artists WHERE foo_user_id=$1`
// error check...
// iterate through all rows to get an array of []TopArtists
defer rows.Close()
for rows.Next() {
topArtist := new(TopArtists)
err := rows.Scan(&topArtist.Id, &topArtist.FooUserId, &topArtist.CreatedAt, &topArtist.TimeSpan, &topArtist.Artists)
if err != nil {
log.Fatalf("Something went wrong %v", err)
}
topArtists = append(topArtists, *topArtist)
}
}
To represent this data in Go I created the following structs
// Represents a row
type TopArtists struct {
Id int64 `json:"id" db:"id"`
FooUserId string `json:"foo_user_id" db:"foo_user_id"`
CreatedAt string `json:"created_at" db:"created_at"`
TimeSpan string `json:"time_span" db:"time_span"`
Artists []Artist `json:"items" db:"items"`
}
// Represents the artist column
type Artist struct {
ArtistName string `json:"artist_name"`
ArtistId string `json:"foo_artist_id"`
ArtistImageURL string `json:"artist_image_url"`
ArtistRank int `json:"artist_rank"`
}
When I call the function that does the query (the one I described above). I get the following error.
Scan error on column index 4, name "items": unsupported Scan, storing driver.Value type []uint8 into type *[]database.Artist.
I have a Value() function, but I am unsure how to implement a Scan() function for the array of the custom struct I have made.
Here is my Value() function, I have attempted to read documentation and similar posts on scanning arrays of primitive types (strings, int, etc) but I could not apply the logic to custom PostgreSQL types.
func (a Artist) Value() (driver.Value, error) {
s := fmt.Sprintf("(%s, %s, %s, %d)",
a.ArtistName,
a.FooArtistId,
a.ArtistImageURL,
a.ArtistRank)
return []byte(s), nil
}
#mkopriva - ...You need to declare a slice type, e.g. type ArtistSlice []Artist,
use that as the field's type, and implement the Value/Scan methods on
that.
Created custom Composite Types Artist in Postgresq has a strict struct as
{(david,38,url,1),(david2,2,"url 2",2)}
then you have to implement Value/Scan method with custom marshal/unmarshal algorithm
For example
type Artists []Artist
func (a *Artists) Scan(value interface{}) error {
source, ok := value.(string) // input example 👉🏻 {"(david,38,url,1)","(david2,2,\"url 2\",2)"}
if !ok {
return errors.New("incompatible type")
}
var res Artists
artists := strings.Split(source, "\",\"")
for _, artist := range artists {
for _, old := range []string{"\\\"","\"","{", "}","(",")"} {
artist = strings.ReplaceAll(artist, old, "")
}
artistRawData := strings.Split(artist, ",")
i, err := strconv.Atoi(artistRawData[1])
if err != nil {
return fmt.Errorf("parce ArtistRank raw data (%s) in %d iteration error: %v", artist, i, err)
}
res = append(res, Artist{
ArtistName: artistRawData[0],
ArtistId: artistRawData[1],
ArtistImageURL: artistRawData[2],
ArtistRank: i,
})
}
*a = res
return nil
}
I have an Employee struct which looks like this:
type Employee struct {
firstName string
lastName string
role string
}
I read from an Excel file a row, which has the corresponding columns in the same order.
From the excel file I created an array that holds this data, so the array looks like this:
[personFirstName, personLastName, personRole]
And I want to initialize my Employee struct with it.
But I can't figure out how, because I can't iterate over the Employee struct like it's an array.
I thought about doing it manually, like this:
e := {
firstName: array[0],
lastName: array[1],
role: array[2],
}
But I guess there should be a better way.
Any Ideas?
There's nothing wrong with the code in the question. Here's how to use the reflect API to accomplish the goal:
Export the field to make the fields settable through the reflect API.
type Employee struct {
FirstName string
LastName string
Role string
}
Create reflect value for an employee. The & and Elem()
shenanigans are required to get a settable value.
var e Employee
v := reflect.ValueOf(&e).Elem()
For each field, set the string:
for i := 0; i < v.NumField(); i++ {
v.Field(i).SetString(values[i])
}
Run an example on the Go Playground.
Put the code in a function if you need it in more than one place:
// setStrings sets the fields of the struct pointed
// to by ptr to the specified values.
func setStrings(ptr interface{}, values []string) {
v := reflect.ValueOf(ptr).Elem()
for i := 0; i < v.NumField(); i++ {
v.Field(i).SetString(values[i])
}
}
Call it like this:
values := []string{"Monica", "Smith", "Manager"}
var e Employee
setStrings(&e, values)
fmt.Printf("%+v\n", e) // prints {FirstName:Monica LastName:Smith Role:Manager}
Run an example on the playground.
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)
}
Let's say there's the following Unload struct, which comes as a single element response from microservice A, and where each Item initially has an empty Units slice:
type Unload struct {
UnloadCode string
Orders []Order
}
type Order struct {
OrderCode string
Items []Item
}
type Item struct {
ItemCode string
Units []string
}
And also a ItemUnit struct which comes as a slice response from microservice B:
type ItemUnit struct {
ItemCode string
Units []Unit
}
type Unit struct {
UnitName string
}
And we need to populate the Item, Units slice with it's corresponding UnitName values, based on similar ItemCodes on both sides.
I've managed to come up with the following solution, for solving this issue:
for orderIndex, order := range unload.Orders {
for itemIndex, item := range order.Items {
for _, itemUnit := range itemUnits {
if item.ItemCode == itemUnit.ItemCode {
for _, unit := range itemUnit.Units {
unload.Orders[orderIndex].Items[itemIndex].Units = append(unload.Orders[orderIndex].Items[itemIndex].Units, unit.UnitName)
}
}
}
}
}
I'm not a go expert myself, but it seems to me that this solution comes with a very high time complexity cost. Would there be any other, more elegant and possibly with a smaller time complexity, way of solving this issue?
*Keep in mind that I can't change the structure of any of my structs.
First, create a map for ItemUnit where itemUnit.ItemCode as key and slice of UnitName as value
itemUnitmap := make(map[string][]string)
for _, itemUnit := range itemUnits {
var units []string
for _, unit := range itemUnit.Units {
units = append(units, unit.UnitName)
}
itemUnitmap[itemUnit.ItemCode] = units
}
Then use map to get slice of UnitName using item.ItemCode. Add slice into Item.Units using variadic function
for orderIndex, order := range unload.Orders {
for itemIndex, item := range order.Items {
if units, ok := itemUnitmap[item.ItemCode]; ok {
unload.Orders[orderIndex].Items[itemIndex].Units = append(unload.Orders[orderIndex].Items[itemIndex].Units, units...)// variadic function used to append slice into slice
}
}
}
I have a struct that contains a slice of a struct and I am trying to retrieve the data from the inner struct. Here is an example of the struct:
type Data struct {
Quotes []struct {
Direct bool `json:"Direct"`
Legs struct {
ID int `json:"Id"`
} `json:"Legs"`
}
}
From the above code I would like to retrieve the value in ID. Here is what I have tried already:
for _, v := range Data.Quotes.Legs {
fmt.Println(v.ID)
}
But I get the following error:
Has no field or method Legs
This works if I just want the value in Direct:
for _, v := range Data.Quotes {
fmt.Println(v.Direct)
}
Does anyone have suggestions on how to do this?
Data.Quotes.Legs is not an array. Data.Quotes is:
var data Data
...
for _, v := range data.Quotes {
fmt.Println(v.Legs.ID)
}