Loop over a slice of structs and update value - pointers

I am getting data from source A and storing it in a slice of structs like so:
type ProductPrice struct {
Type string
Sku string
UnitPrice string
PriceList string
standardPrice string
specialPrice string
specialStart string
specialEnd string
pricingUnit string
categoryCode string
isOnSpecial bool
}
func getProductPricesFromDatabase(instance string) []ProductPrice {
rows, err := myDBConnection.Query(// My query here)
if err != nil {
log.Fatal("There was an issue with the query for product price: ", err)
}
defer rows.Close()
var productPrices []ProductPrice
for rows.Next() {
var product = ProductPrice{}
err := rows.Scan(
&product.Type,
&product.Sku,
&product.standardPrice,
&product.specialPrice,
&product.specialStart,
&product.specialEnd,
&product.pricingUnit,
&product.PriceList,
&product.categoryCode,
)
if err != nil {
log.Fatal("product price scan error: ", err)
}
productPrices = append(productPrices, product)
}
return productPrices
}
I am then getting some data from source B and storing it in a slice of structs like so:
type ContractProductPrice struct {
CustID string
PriceBy string
AppliesTo string
PriceList string
StartDate string
EndDate string
PricingAdjustmentType string
PricingAdjustmentValue string
UseLowest string
}
func getContractProductPricesFromDatabase(instance string) []ContractProductPrice {
rows, err := myDBConnection.Query(// My query here)
if err != nil {
log.Fatal("There was an issue with the query for contract product price: ", err)
}
defer rows.Close()
var contractProductPrices []ContractProductPrice
for rows.Next() {
var product = ContractProductPrice{}
err := rows.Scan(
&product.CustID,
&product.PriceBy,
&product.AppliesTo,
&product.PriceList,
&product.StartDate,
&product.EndDate,
&product.PricingAdjustmentType,
&product.PricingAdjustmentValue,
&product.UseLowest,
)
if err != nil {
log.Fatal("contract product price scan error: ", err)
}
contractProductPrices = append(contractProductPrices, product)
}
return contractProductPrices
}
After getting the data from source B, I am wanting to update the slice of structs from source A with some data from source B.
productPrices := getProductPricesFromDatabase(instance)
contractProductPrices := getContractProductPricesFromDatabase(instance)
processedProductPrices := processProductPricesFromDatabase(productPrices, contractProductPrices)
func processProductPricesFromDatabase(productPrices []ProductPrice, contractProductPrices []ContractProductPrice) []ProductPrice {
// Loop over contact prices and update relevant product prices
for _, contractPrice := range contractProductPrices {
for _, product := range productPrices {
if contractPrice.AppliesTo == product.Sku {
product.UnitPrice = contractPrice.PricingAdjustmentValue
}
}
}
return productPrices
}
However, after this runs, the unit prices in processedProductPrices is still empty.
From my searching, I understand what the issue is; Go passes by value and so I am not updating the original memory address and so the values are not changing.
However, I do not understand/know what I need to change to fix this given I am working with a slice of structs rather than a simpler example of a slice of number/strings etc.
How can I update productPrices so that when I return it, processedProductPrices is equal to the updated productPrices slice of structs?

Anytime you're dealing with values that you know you'll need to modify, it is best, at least in my opinion, to use pointers. They'll make your life easier.
So instead of:
func getProductPricesFromDatabase(instance string) []ProductPrice {
// ...
var productPrices []ProductPrice
for rows.Next() {
var product = ProductPrice{}
// ...
}
return productPrices
}
I would recommend you refactor your code to:
func getProductPricesFromDatabase(instance string) []*ProductPrice {
// ...
var productPrices []*ProductPrice
for rows.Next() {
var product = new(ProductPrice)
// ...
}
return productPrices
}
Now do the same with getContractProductPricesFromDatabase and finally update the argument types to your processProductPricesFromDatabase function:
func processProductPricesFromDatabase(productPrices []*ProductPrice, contractProductPrices []*ContractProductPrice) []*ProductPrice {
// Loop over contact prices and update relevant product prices
for _, contractPrice := range contractProductPrices {
for _, product := range productPrices {
if contractPrice.AppliesTo == product.Sku {
product.UnitPrice = contractPrice.PricingAdjustmentValue
}
}
}
return productPrices
}
As an alternative, if you want to keep using non-pointer types, you can directly modify the values referenced by the slice by indexing into it.
func processProductPricesFromDatabase(productPrices []ProductPrice, contractProductPrices []ContractProductPrice) []ProductPrice {
// Loop over contact prices and update relevant product prices
for _, contractPrice := range contractProductPrices {
for i, _ := range productPrices {
if contractPrice.AppliesTo == productPrices[i].Sku {
productPrices[i].UnitPrice = contractPrice.PricingAdjustmentValue
}
}
}
return productPrices
}

Related

how do I query a sqlite db in go? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
I'm trying to get an array of "Search" structs.
type Searches struct {
id int
count int
search string
}
func get_recent_searches(db *sql.DB, limit int) []Searches {
var searches []Searches
var query = "select from searches brder by count desc limit ?"
st, err := db.Prepare(query)
result, err := st.Exec(limit)
if err != nil { panic(err) }
/*
for rows.Next() {
item := Searches{}
err2 := rows.Scan(&item.id, &item.count, &item.search)
if err2 != nil { panic(err2) }
searches = append(searches, item)
}
*/
return result
}
Here is the error:
src/main.go:72:2: cannot use result (type sql.Result) as type []Searches in return argument
package main
import (
"fmt"
"log"
"database/sql"
_ "github.com/mattn/go-sqlite3" // Import go-sqlite3 library
)
// Searches ...
type Searches struct {
id int
count int
search string
}
func getRecentSearches(db *sql.DB, limit int) []Searches {
var searches []Searches
row, err := db.Query("SELECT * FROM search ORDER BY count LIMIT ?", limit)
if err != nil {
log.Fatal(err)
}
defer row.Close()
for row.Next() { // Iterate and fetch the records from result cursor
item := Searches{}
err := row.Scan(&item.id, &item.count, &item.search)
if err != nil {
log.Fatal(err)
}
searches = append(searches, item)
}
return searches
}
func main() {
searchDB, _ := sql.Open("sqlite3", "./search.db") // Open the created SQLite File
defer searchDB.Close() // Defer Closing the database
// Get and print the first 5 records to the console
searches := getRecentSearches(searchDB, 4)
fmt.Printf("ID\tCount\tSearch\n")
for _, item := range searches {
fmt.Printf("%d\t%d\t%s\n", item.id, item.count, item.search)
}
}
This link explains how to use SQLite with go: https://astaxie.gitbooks.io/build-web-application-with-golang/content/en/05.3.html
But the error you are seeing is because your function is defined as
func get_recent_searches(db *sql.DB, limit int) []Searches {}
It must return []Searches (a slice of Searches structs). (NB I would probably name a single search result as Search).
Adding the fixes suggested by #Peter Gloor (Query to return rows, rather than an Exec to change data) and removing the limit placeholder, your code could be
func get_recent_searches(db *sql.DB, limit int) []Searches {
searches := make([]Searches,0)
query := "select * from searches order by count desc"
st, err := db.Prepare(query)
if err != nil { panic(err) }
rows, err := st.Query(limit)
if err != nil { panic(err) }
for rows.Next() {
item := Searches{}
err2 := rows.Scan(&item.id, &item.count, &item.search)
if err2 != nil { panic(err2) }
searches = append(searches, item)
}
return searches
}

How to modify a property value of a nested struct

I'm trying to modify the value of a nested struct's variable in Go. Basically, I want to modify the RsvpString property but GetRsvp() seems to return the value of Rsvp instead of a reference, so when I modify its property value, it doesn't get reflected in the Event instance.
The test is below.
type Event struct {
Rsvps []Rsvp `json:"rsvps"`
}
type Rsvp struct {
UserId string `json:"userId"`
RsvpString string `json:"rsvp"`
}
func (e *Event) GetRsvp(userId string) (rsvp *Rsvp, err error) {
for _, element := range e.Rsvps {
if element.UserId == userId {
return &element, nil
}
}
return &Rsvp{}, fmt.Errorf("could not find RSVP based on UserID")
}
func (e *Event) UpdateExistingRsvp(userId string, rsvpString string) {
rsvp, err := e.GetRsvp(userId)
if err == nil {
rsvp.RsvpString = rsvpString
}
}
Here's the test code:
func TestEvent_UpdateExistingRsvp(t *testing.T) {
e := Event{[]Rsvp{
{Name: "Bill",
UserId: "bill",
Rsvp: "yes"}}}
e.UpdateExistingRsvp("bill", "no")
assert.Equal(t, "no", e.Rsvps[0].Rsvp, "RSVP should be switched to no") // fails
}
GetRsvp is returning the address of the loop variable, not the address of the element in the array. To fix:
for i, element := range e.Rsvps {
if element.UserId == userId {
return &e.Rsvps[i], nil
}
}
The loop variable keeps a copy of e.Rsvps[i], and it gets overwritten at every iteration. If you return the address of the loop variable, then you return the address of that copy.
When ranging over a slice, two values are returned for each iteration. The first is the index, and the second is a copy of the element at that index.
so technically you are trying to modify the copy of the Rsvp.
instead, return the index and from the GetRsvp() method and update.
func (e *Event) GetRsvp(userId string) (int, error) {
for index , element := range e.Rsvps {
if element.UserId == userId {
return index, nil
}
}
return -1 , fmt.Errorf("could not find RSVP based on UserID")
}
func (e *Event) UpdateExistingRsvp(userId string, rsvpString string) {
index, err := e.GetRsvp(userId)
if err != nil || index == -1 {
fmt.Println("no such user")
}
e.Rsvps[index].RsvpString = rsvpString
}

How to detect an empty result with Go Firebase-Admin SDK when data at the path doesn't exists

I'm using the following code to get an object from a Firebase realtime database.
type Item struct {
title string `json:"title"`
}
var item Item
if err := db.NewRef("/items/itemid").Get(ctx, &item); err != nil {
log.Infof(ctx, "An error occured %v", err.Error())
}
log.Infof(ctx, "Item %v", item)
If no data exists at the given path in the realtime database the SDK will not return an error, instead I will end up with an empty struct in the variable item.
What would be the cleanest/most readable way to detect that the data at the path is not there?
I've searched for hours but couldn't find a clear cut answer to this question.
Here's one way to solve this problem:
type NullableItem struct {
Item struct {
Title string `json:"title"`
}
IsNull bool
}
func (i *NullableItem) UnmarshalJSON(b []byte) error {
if string(b) == "null" {
i.IsNull = true
return nil
}
return json.Unmarshal(b, &i.Item)
}
func TestGetNonExisting(t *testing.T) {
var i NullableItem
r := client.NewRef("items/non_existing")
if err := r.Get(context.Background(), &i); err != nil {
t.Fatal(err)
}
if !i.IsNull {
t.Errorf("Get() = %v; want IsNull = true", i)
}
}
As a best practice you should also implement MarshalJSON() function.

how to combine regular FormValue with multipart form data in Golang net/http Request

I want to call FormValue on golang net/http Request from several middleware handler functions before serving the request. And I do not want to invalidate the request while doing this.
It works fine except when the incoming request has a multipart form-data, the data gets invalidated after I call FormValue and there is nothing to parse in the final route.
I wrote a utility function that solved my problem:
package utils
import (
"bytes"
"io"
"io/ioutil"
"mime"
"mime/multipart"
"net/http"
"strings"
)
// Get form values without invalidating the request body in case the data is multiform
func GetFormValues(request *http.Request, keys []string) []string {
var values []string
mediaType, params, err := mime.ParseMediaType(request.Header.Get("Content-Type"))
if err != nil || !strings.HasPrefix(mediaType, "multipart/") {
for i := range keys {
values = append(values, request.FormValue(keys[i]))
}
} else { // multi form
buf, _ := ioutil.ReadAll(request.Body)
origBody := ioutil.NopCloser(bytes.NewBuffer(buf))
var rdr = multipart.NewReader(bytes.NewBuffer(buf), params["boundary"])
for len(values) < len(keys) {
part, err_part := rdr.NextPart()
if err_part == io.EOF {
break
}
for i := range keys {
if part.FormName() == keys[i] {
buf := new(bytes.Buffer)
buf.ReadFrom(part)
values = append(values, buf.String())
}
}
}
request.Body = origBody
}
if len(values) == len(keys) {
return values
} else {
return nil
}
}
// Get form value without invalidating the request body in case the data is multiform
func GetFormValue(request *http.Request, key string) string {
if result := GetFormValues(request, []string{key}); len(result) == 1 {
return result[0]
} else {
return ""
}
}
Now instead of calling
value := request.FormValue(key)
I do
value := utils.GetFormValue(request, key)
or for multiple values
values := utils.GetFormValues(request, []string{keys...})

Display Foreign Keys of Foreign Keys in Go GORM

I was able to partially solve this with this case
Unfortunately, the Preload() function doesn't seem to work with delving further down in the related object set.
To clarify, I have the following models:
type Room struct {
gorm.Model
Name string
Games []Game `gorm:"ForeignKey:RoomID"`
}
type Game struct {
gorm.Model
RoomID int `gorm:"index"`
Players []Player `gorm:"ForeignKey:GameID"`
}
type Player struct {
gorm.Model
Name string
GameID int `gorm:"index"`
}
When I create a new object with a new Room, Game, and Player object created I get the following data returned (json encoded):
{"Value":{"ID":26,"CreatedAt":"2016-05-15T01:21:22.426234189-07:00","UpdatedAt":"2016-05-15T01:21:22.426234189-07:00","DeletedAt":null,"Name":"foo","Games":[{"ID":17,"CreatedAt":"2016-05-15T01:21:22.427026134-07:00","UpdatedAt":"2016-05-15T01:21:22.427026134-07:00","DeletedAt":null,"RoomID":26,"Turns":null,"Players":[{"ID":4,"CreatedAt":"2016-05-15T01:21:22.427560561-07:00","UpdatedAt":"2016-05-15T01:21:22.427560561-07:00","DeletedAt":null,"Name":"TestPlayer","GameID":17}],"Counter":1,"Assigned":false}],"Testing":false},"Error":null,"RowsAffected":1}
But if I try to query the structure with a preload() function, I get the following:
{"Value":{"ID":26,"CreatedAt":"2016-05-15T01:21:22.426234189-07:00","UpdatedAt":"2016-05-15T01:21:22.426234189-07:00","DeletedAt":null,"Name":"foo","Games":[{"ID":17,"CreatedAt":"2016-05-15T01:21:22.427026134-07:00","UpdatedAt":"2016-05-15T01:21:22.427026134-07:00","DeletedAt":null,"RoomID":26,"Turns":null,"Players":null,"Counter":1,"Assigned":false}],"Testing":false},"Error":null,"RowsAffected":1}
Note that the Players section is now null. Here's my room registration and room query functions:
func RegisterRoom(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
// Handle error
}
// r.PostForm is a map of our POST form values
room := Room{
Name: r.PostFormValue("label"),
Games: []Game{{
Counter: 1,
Players: []Player{{Name: r.PostFormValue("username")}},
}},
}
t := db.Create(&room)
if err := json.NewEncoder(w).Encode(t); err != nil {
panic(err)
}
}
func RoomShow(w http.ResponseWriter, r *http.Request) {
var rm Room
vars := mux.Vars(r)
roomId := vars["roomId"]
id, _ := strconv.Atoi(roomId)
room := db.Preload("Games").First(&rm, id)
result := db.Find(&room)
json.NewEncoder(w).Encode(result)
}
Solved it! Answer was under Nested Preloading smacks forehead

Resources