-2

I have two different struct as mentioned below A abd B and two process functions. Is there any way by means of which i can write a common function to generate the map[string]struct for the both the struct. Moreover, is there any way using reflection given the struct name i can create the object of the same?

type A struct {
    name string
    // more fields
}


type B struct {
    name string
    // more fields  
}

func ProcessA(input []A) map[string]A {
    output := make(map[string]A)
    for _, v := range input {
         output[v.name] = v
    }
  return output
}


func ProcessB(input []B) map[string]B {
    output := make(map[string]B)
    for _, v := range input {
         output[v.name] = v
    }
  return output
}
Naresh
  • 5,073
  • 12
  • 67
  • 124
  • can anyone exlpain the reason of downvote? – Naresh Jun 08 '18 at 15:16
  • 1
    Assume the tooltip for the downvote button is the reason if there are no specific comments. As for the questions, no Go does not have generics, and the types are invariant. Yes, you can use reflection; have you tried? – JimB Jun 08 '18 at 15:30
  • 1
    *"Moreover, is there any way using reflection given the struct name i can create the object of the same?"* If by name you mean as a string value, then no, not with just the name. You'll also need a list of the fields and their types and I believe the package name too, and you would still have to have access to the actual type to be able to convert the result at the end. – mkopriva Jun 08 '18 at 15:44
  • @JimB Yes, i did try to achieve the same using interface. But later i realised it can't be done using interface due to `https://stackoverflow.com/questions/12994679/golang-slice-of-struct-slice-of-interface-it-implements`. So, is there any way by means of which using reflection it can be done. Moreover, I still didn't understand the reason of downvotes. – Naresh Jun 08 '18 at 18:59
  • @Naresh: The downvote button says "this question does not show any research effort", and this question has been asked dozens of time in many permutations. Of course this is trivial with reflection, but in practice this is rarely a concern, since putting that simple for-loop inline in a couple places is much clearer than the reflection. – JimB Jun 08 '18 at 19:10

2 Answers2

0

Idiomatic way in Go would be to use interface.

type Named interface {
  Name() string
}

type letter struct {
  name string
}

func (l letter) Name() string {
  return l.name
}


type A struct {
    letter
    // more fields
}


type B struct {
    letter
    // more fields  
}

func ProcessNameds(input []Named) map[string]Named {
    output := make(map[string]Named, len(input))
    for _, v := range input {
         output[v.Name()] = v
    }
  return output
}
Alexander Trakhimenok
  • 6,019
  • 2
  • 27
  • 52
  • it will not work. `slice of struct != slice of interface it implements ` and here is the reference `https://stackoverflow.com/questions/12994679/golang-slice-of-struct-slice-of-interface-it-implements` – Naresh Jun 08 '18 at 18:55
  • @Naresh: this isn't requiring covariance of maps, the idea is that you only use `[]Named` throughout. – JimB Jun 08 '18 at 19:02
  • You could cast back to required type if needed or use/еxtend interface – Alexander Trakhimenok Jun 09 '18 at 00:05
  • @AlexanderTrakhimenok cannot use (type []A) as type []Named in argument. I tried what you suggested but it still gives the same error. As i mentioned earlier you can't pass a slice of struct in the function which take slice of interface as an agrument – Naresh Jun 10 '18 at 09:50
  • I was saying about casting A to Named and back, not about []A and []Named. – Alexander Trakhimenok Jun 11 '18 at 01:42
0

Well, see if something like this would help:

package main

import (
    "fmt"
    "strconv"
)

type A struct {
    name string
    // more fields
}

type B struct {
    name string
    // more fields
}

func Process(x interface{}) interface{} {
    ma := make(map[string]int)
    mb := make(map[string]string)

    if x == nil {
        return nil
    } else if a, ok := x.([]A); ok {
        fmt.Printf("Type A argument passed %s\n", x)
        ma[a[0].name] = 1
        ma[a[1].name] = 2
        return ma //you can return whatever type you want here
    } else if b, ok := x.([]B); ok {
        fmt.Printf("Type B argument passed %s\n", x)
        mb[b[0].name] = "a"
        mb[b[1].name] = "b"
        return mb //you can return whatever type you want here
    } else {
        panic(fmt.Sprintf("Unexpected type %T: %v", x, x))
    }
    return nil
}

func main() {
    a := make([]A, 5)
    for i := 0; i < len(a); i++ {
        a[i].name = strconv.Itoa(i) + "A"
    }
    b := make([]B, 7)
    for i := 0; i < len(b); i++ {
        b[i].name = strconv.Itoa(i) + "B"
    }

    fmt.Println(Process(a))
    fmt.Println(Process(b))
    //Uncomment line below to see the panic
    //fmt.Println(Process(8))

}

https://play.golang.org/p/irdCsbpvUv_t

Ravi R
  • 1,692
  • 11
  • 16