Home > OS >  Why my array of struct is not updating elements golang
Why my array of struct is not updating elements golang

Time:10-15

What I'm doing wrong here? Is there some pointer that I'm missing or I missed some logic?

package main

import (
    "fmt"
)

type Foo struct {
    name string
    age  string
}

type FooList struct {
    group string
    foos  []Foo
}

func (f *FooList) addFoo(foo Foo) {
    f.foos = append(f.foos, foo)
}

func main() {
    data := [][]string{{"1a", "11", "12"}, {"1a", "13", "14"}, {"2b", "21", "22"}}
    var resultFoo []FooList

    for _, d := range data {
        if !fInFoo(resultFoo, d[0]) {
            foL := FooList{group: d[0]}
            resultFoo = append(resultFoo, foL)
        }
    }

    for _, d := range data {
        fooList := getFooList(d[0], resultFoo)
        foo := Foo{name: d[1], age: d[2]}
        fooList.addFoo(foo)
    }
    fmt.Println(resultFoo)
}

func fInFoo(fooList []FooList, foo string) bool {
    for _, fl := range fooList {
        if fl.group == foo {
            return true
        }
    }
    return false
}

func getFooList(foo string, listOfFoo []FooList) *FooList {
    for _, f := range listOfFoo {
        if f.group == foo {
            return &f
        }
    }
    panic("Couldn't find foo!!")
}

resultFoo always print this result [{1a []} {2b []}] And I expect this [{1a [{11 12} {13 14}]} {2b [{21 22}]}]. Any suggestion is acceptable to solve this problem :)

CodePudding user response:

The problem is in the getFooList function. You are returning a copy of the struct, not a pointer to the struct. This means that when you call addFoo on the returned struct, you are modifying a copy, not the original struct.

To fix this, you can either return a pointer to the struct:

func getFooList(foo string, listOfFoo []FooList) *FooList {
    for _, f := range listOfFoo {
        if f.group == foo {
            return &f
        }
    }
    panic("Couldn't find foo!!")
}

Or you can modify the addFoo function to take a pointer:

func (f *FooList) addFoo(foo *Foo) {
    f.foos = append(f.foos, *foo)
}

CodePudding user response:

This is a little bit of a "gotcha". When you take the pointer of f in getFooList, you're actually taking a pointer to a copy used in the for range loop and not the copy in your slice.

func getFooList(foo string, listOfFoo []FooList) *FooList {
    for i, f := range listOfFoo {
        if f.group == foo {
            // using &f here gives a pointer to a copy of FooList,
            // but we want a pointer to the copy in our slice
            return &listOfFoo[i]
        }
    }
    panic("Couldn't find foo!!")
}

Run it in the playground.

Additionally, this seems like a better spot for a map if you can use it.

type Foo struct {
    name string
    age  string
}

func main() {
    data := [][]string{{"1a", "11", "12"}, {"1a", "13", "14"}, {"2b", "21", "22"}}
    resultFoo := make(map[string][]Foo)

    for _, d := range data {
        group := d[0]
        resultFoo[group] = append(resultFoo[group], Foo{name: d[1], age: d[2]})
    }

    fmt.Println(resultFoo)
}

Run it on the playground.

  • Related