0

Is there a way to iterate over a slice in a generic way using reflection?

type LotsOfSlices struct {
    As []A
    Bs []B
    Cs []C
    //.... and lots more of these
}

type A struct {
    F string
    //.... and lots of other stufff that's different from the other structs
}

type B struct {
    F string
    //.... and lots of other stufff that's different from the other structs
}

type C struct {
    F string
    //.... and lots of other stufff that's different from the other structs
}

I want to use reflection to reduce code complexity and duplicate code. Is this possible? Is this a bad idea?

For example, not this:

func processData(l LotsOfSlice){
    for _, a := range l.As{
        // use a.F
    }
    for _, b := range l.Bs{
        // use b.F
    }
    for _, c := range l.Cs{
        // use c.F
    }
    ...
}

But something like this instead:

func processData(l LotsOfSlices){
    t := reflect.TypeOf(l)
    for i := 0; i < t.NumField(); i++ {
        zs := reflect.ValueOf(l).Field(i).Interface()
        for _, z := range zs{
            // use z.F
        }
    }
}

2 Answers2

2

Use Value.Len and Value.Index to iterate over an array or slice:

func processData(l LotsOfSlices) {
    v := reflect.ValueOf(l)
    for i := 0; i < v.NumField(); i++ {
        f := v.Field(i)
        if f.Kind() != reflect.Slice {
            continue
        }
        for i := 0; i < f.Len(); i++ {
            e := f.Index(i)
            s := e.FieldByName("F")
            // Do something with s
        }
    }
}
Charlie Tumahai
  • 113,709
  • 12
  • 249
  • 242
0

if your structs do similar end result (returns int or operates on strings) but does so uniquely for each struct type you can define functions on them:

func (a *A) GetResult() int { // sums two numbers
    return a.p1 + a.p2
}

func (b *B) GetResult() int { // subtracts two numbers
    return b.p1 - b.p2
}

func (c *C) GetResult() int { // times two numbers
    return c.p1 * c.p2
}

then define an interface Operable

type Operable interface {
    GetResult() int // shared function
}

then make a function that accepts the interface as argument, and any struct that implements all functions in that interface can be accepted into it as an argument

func processOperable(o []Operable){
    for _, v := range o{
        v.GetResult() --> unique for each struct
    }
}
M-Raw
  • 779
  • 4
  • 10