2016-07-04 122 views
9

Sto cercando di creare una funzione che poteva accettare seguendointerfaccia Converti {} per mappare in Golang

*struct 
[]*struct 
map[string]*struct 

Qui struct potrebbe essere qualsiasi non struct solo uno specifico. La conversione dell'interfaccia in *struct o []*struct funziona correttamente. Ma dando errore per la mappa.

Dopo aver riflettuto mostra che è map [] ma che genera un errore quando tenta di scorrere su un intervallo.

Ecco il codice

package main 

import (
    "fmt" 
    "reflect" 
) 

type Book struct { 
    ID  int 
    Title string 
    Year int 
} 

func process(in interface{}, isSlice bool, isMap bool) { 
    v := reflect.ValueOf(in) 

    if isSlice { 
     for i := 0; i < v.Len(); i++ { 
      strct := v.Index(i).Interface() 
      //... proccess struct 
     } 
     return 
    } 

    if isMap { 
     fmt.Printf("Type: %v\n", v)  // map[] 
     for _, s := range v {   // Error: cannot range over v (type reflect.Value) 
      fmt.Printf("Value: %v\n", s.Interface()) 
     } 
    }  
} 

func main() { 
    b := Book{} 
    b.Title = "Learn Go Language" 
    b.Year = 2014 
    m := make(map[string]*Book) 
    m["1"] = &b 

    process(m, false, true) 
} 

Esiste un modo per convertire interface{} per mappare e iterare o scarica di elementi.

risposta

10

Se il mappa del valore può essere di qualsiasi tipo, quindi utilizzare riflettere per scorrere la mappa:

if v.Kind() == reflect.Map { 
    for _, key := range v.MapKeys() { 
     strct := v.MapIndex(key) 
     fmt.Println(key.Interface(), strct.Interface()) 
    } 
} 

playground example

Se ther e 'un piccolo e conosciuto insieme di tipi struct, poi un type switch può essere utilizzato:

func process(in interface{}) { 
    switch v := in.(type) { 
    case map[string]*Book: 
    for s, b := range v { 
     // b has type *Book 
     fmt.Printf("%s: book=%v\n" s, b) 
    } 
    case map[string]*Author: 
    for s, a := range v { 
     // a has type *Author 
     fmt.Printf("%s: author=%v\n" s, a) 
    } 
    case []*Book: 
    for i, b := range v { 
     fmt.Printf("%d: book=%v\n" i, b) 
    } 
    case []*Author: 
    for i, a := range v { 
     fmt.Printf("%d: author=%v\n" i, a) 
    } 
    case *Book: 
    fmt.Ptintf("book=%v\n", v) 
    case *Author: 
    fmt.Printf("author=%v\n", v) 
    default: 
    // handle unknown type 
    } 
} 
+0

Bello. Ho aggiunto il 'MapKeys' alla mia parte di risposta mentre stavi postando la tua! – cnicutar

+0

Funzionante assolutamente bene. Molte grazie. – SamTech

15

Non è necessario riflettere qui. Prova:

v, ok := in.(map[string]*Book) 
if !ok { 
    // Can't assert, handle error. 
} 
for _, s := range v { 
    fmt.Printf("Value: %v\n", s) 
} 

Lo stesso vale per il resto della funzione. Sembra che tu stia utilizzando la riflessione quando ti servirebbe meglio con uno type switch.


In alternativa, se ti ostini a usare riflessione qui (che non ha molto senso) è anche possibile utilizzare Value.MapKeys con il risultato dalla tua ValueOf (vedi la risposta https://stackoverflow.com/a/38186057/714501)

+1

Qui struct potrebbe essere qualsiasi struct come il libro, Auther, Editore ... per il tipo tipo a commutazione di struct deve essere conosciuto, che ho non ho – SamTech

+0

@SamTech In che modo il tipo di struttura non è "noto"? Puoi semplicemente passare sopra tutti i tipi che vuoi gestire. – cnicutar

+0

Voglio dire, come faccio a sapere se devo digitare passare l'interfaccia per mappare [stringa] * Libro o mappa [stringa] * Auther? – SamTech