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
}
Related
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.
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
}
I have a function
func (r *render) foo(v interface{}) {
val := reflect.ValueOf(v)
fields := structs.Fields(val.Index(0).Interface())
...
Which takes a slice of structs and tries to get the fields of v,
however if v is empty then "val.Index(0)" crashes the program. Is there a better way to do this?
You need to check first if you have a slice to begin with, then check if you have an empty slice, and you probably should check that you have a struct too while you're at it: (example)
val := reflect.ValueOf(v)
if val.Kind() != reflect.Slice {
fmt.Println("not a slice")
return
}
if val.Len() == 0 {
fmt.Println("empty slice")
return
}
if val.Index(0).Kind() != reflect.Struct {
fmt.Println("not a slice of structs")
return
}
fields := structs.Fields(val.Index(0).Interface())
...
If you only want the fields from a struct type, regardless of if the slice is empty, you can use the slice type's Elem method to extract it (example)
// get the internal type of the slice
t := val.Type().Elem()
if t.Kind() != reflect.Struct {
fmt.Println("not a struct")
return
}
fmt.Println("Type:", t)
for i := 0; i < t.NumField(); i++ {
fmt.Println(t.Field(i).Name)
}
I want to have a generic way which will always return the struct value no matter if it is provided as pointer, slice or array.
My approach towards this looks:
func main() {
p := Person{}
if value(p).Kind() != reflect.Struct {
fmt.Printf("Error 1")
}
if value(&p).Kind() != reflect.Struct {
fmt.Printf("Error 2")
}
if value([]Person{p}).Kind() != reflect.Struct {
fmt.Printf("Error 3")
}
if value(&[]Person{p}).Kind() != reflect.Struct {
fmt.Printf("Error 4")
}
}
func value(m interface{}) reflect.Value {
v := reflect.ValueOf(m)
switch v.Kind() {
case reflect.Ptr:
v = v.Elem()
fallthrough
case reflect.Slice, reflect.Array:
v = v.Elem()
}
return v
}
Go Playground
As you can see the problem lays with in getting the struct out of a slice or array.
How do I need to extend the above function to get the struct value from with in an array or slice?
Update: What I want to do is turn []People into People.
If you just want the type even if the slice is nil, you can use something like this:
func value(m interface{}) reflect.Type {
t := reflect.Indirect(reflect.ValueOf(m)).Type()
if t.Kind() == reflect.Slice || t.Kind() == reflect.Array {
t = t.Elem()
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
return t
}
return t
}
About Type.Elem(), from http://golang.org/pkg/reflect/#Type:
// Elem returns a type's element type.
// It panics if the type's Kind is not Array, Chan, Map, Ptr, or Slice.
//edit updated the function to work on a slice of pointers as well.
I assume that what you mean by "get out of the slice or array" is that you want the first element (that is, the element at index 0)? If that's what you want, then you should use the reflect.Value.Index() method. For example:
func value(m interface{}) reflect.Value {
v := reflect.ValueOf(m)
switch v.Kind() {
case reflect.Ptr:
v = v.Elem()
if v.Kind() == reflect.Slice || v.Kind() == reflect.Array {
v = v.Index(0)
}
case reflect.Slice, reflect.Array:
v = v.Index(0)
default:
break LOOP
}
return v
}
Go playground
Note that I also slightly modified the flow logic. You were falling through to the slice/array case from the pointer case. You probably intended for the case condition to be tested again (so it'd effectively say, "if this was a pointer, now check if the thing it pointed to was a slice or an array"), but that's not how fallthrough works. Now it checks the case explicitly.
I am working with golang's pointers the way I did with c++, but it seems not to work, which would be the right way to do it? or what am I doing wrong?, Thanks.
ftw I'm doing AsyncBinaryTrees.
type Obj interface {
Compare(node Obj) int
}
type Tree struct {
Item Obj
Rigth, Left *Tree
height int16
}
func Insert(t *Tree, item Obj) chan struct{} {
done := make(chan struct{}, 1)
go insert(t, item, done)
return done
}
func insert(t *Tree, item Obj, done chan struct{}) {
if t == nil {
t = &Tree{Item: nil, Rigth: nil, Left: nil, height: 0}
var signal struct{}
done <- signal
close(done)
} else {
if t.Item.Compare(item) == 1 { //Left
insert(t.Left, item, done)
} else if t.Item.Compare(item) == -1 { //Rigth
insert(t.Right, item, done)
} else {
close(done)
panic
}
}
}
//=== testing
func assertSignal(ch_signal chan struct{}, t *testing.T) {
_, done := <-ch_signal
if !done {
t.Error("Error: it should send a signal of empty struct")
}
}
func TestInsertion(t *testing.T) {
var tree *Tree
ch_signal := Insert(tree, newObjInt())
fmt.PrintLn(t) //=> <nil>
assertSignal(ch_signal, t) //=>PASS
ch_signal = Insert(tree, newObjInt())
fmt.PrintLn(t) //=> <nil>
assertSignal(ch_signal, t) //=>PASS
ch_signal = Insert(tree, newObjInt())
fmt.PrintLn(t) //=> <nil>
assertSignal(ch_signal, t) //=>PASS
ch_signal = Insert(tree, newObjInt())
assertSignal(ch_signal, t) //=>PASS
}
nil
nil
nil
TEST PASS
In your insert function you have:
func insert(t *Tree, item Obj, done chan struct{}) {
if t == nil {
t = &Tree{Item: nil, Rigth: nil, Left: nil, height: 0}
...
}
This updates the local variable t, but will not change the variable passed in the calling scope since Go passes function parameters by value. So when you make the following call:
insert(t.Left, item, done)
if t.Left is nil, its value will not be changed by the function call. If you do want it to update the variable, you'll need to define the function argument as t **Tree, change references to set *t instead, and change the call to:
insert(&t.Left, item, done)
There is no equivalent to C++'s syntax for passing function arguments by reference: instead you need to be explicit when passing pointers.