1

I want to create a function that takes either a map or an array of whatever and iterates over it calling a function on each item which knows what to do with whatever types it encounters.

Here's my first failed attempt. Currently when I run it in my real use case it always says "uh oh!".

func DoTheThingToAllTheThings(data_interface interface{}) int {
    var numThings int

    switch data := data_interface.(type) {
    case map[interface{}]interface{}:
        numThings = len(data)
        for index, item := range data {
            DoTheThing(index, item)
    }
    case []interface{}:
        numThings = len(data)
        for index, item := range data {
            DoTheThing(index, item)
        }
    default:
        fmt.Println("uh oh!")
    }

    return numThings
}

The array or map could contain lots of different things so it isn't an option to try and match every possible input.

Stated otherwise, is there a way to iterate over an array or map in Go without knowing exactly what it is?

Nat
  • 2,689
  • 2
  • 29
  • 35
  • 1
    The `reflect`-based solutions below will work, but the idiomatic thing is just to write out the loop at the callsite if possible. – twotwotwo Sep 13 '14 at 22:00

2 Answers2

1

For your example to work, you would need to build an array of interface{} (or a map), in order for the right type to be detected:

// This won't work, as the .(type) would be []S
someS := []S{S{}}
DoTheThingToAllTheThings(someS)

// This will: it will go in case []interface{}:
someSI := make([]interface{}, len(someS))
for i, s := range someS {
    someSI[i] = s
}
DoTheThingToAllTheThings(someSI)

See a full example here.

But that means you would still work with interface{} in your DoTheThing function.
There is no real generic here, as I mentioned in "What would generics in Go be?".

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
1

The function fmt.Printf("%v\n", data_interface) does exactly what you want. It will print the whole map or array passed to it.

You can find the implementation here: http://golang.org/src/pkg/fmt/print.go?h=printArg#L708

The line near the end of printArg is key:

return p.printReflectValue(reflect.ValueOf(arg), verb, plus, goSyntax, depth

It's using the "reflect" package: http://golang.org/pkg/reflect/ to query the type of the argument. Inside p.printReflectValue here: http://golang.org/src/pkg/fmt/print.go?h=printArg#L862 You will see the two cases where maps and structures are dealt with. It then uses recursion through printValue to manage the contents.

Here's a bit of code that takes the reflection of a structure and then turns it back into another variable of the right type. You can't change from one arbitrary type to another using this, it has to be legal in go to cast the types from one to another without using reflection.

package main

import (
    "fmt"
    "reflect"
)

type Player string

type Board struct {
    Tboard  [9]string
    Player1 Player
    Player2 Player
}

// ignore this function contents, I grabbed it from elsewhere.
func makeBoard() *Board {
    b := &Board{Tboard: [9]string{}}
    for x := 0; x < len(b.Tboard); x++ {
        b.Tboard[x] = "X"
        fmt.Println(b.Tboard[x])
    }
    b.Player1 = "George"
    b.Player2 = "Tim"

    fmt.Printf("Len: %v\n", len(b.Tboard)) // => 9

    fmt.Printf("Contents: %v\n", b)
    fmt.Printf("Syntax: %#v\n", b)
    fmt.Printf("Type: %T\n", b)
    fmt.Println("Board:", b.Tboard)
    return b
}

func main() {
    myBoard := makeBoard()

    v := reflect.ValueOf(*myBoard) // v is of type Value
    t := v.Type()

    fmt.Printf("Value: %v %T\n", v, v)
    fmt.Printf("Type:  %v %T\n", t, t)

    // would be a switch
    if t == reflect.TypeOf(*myBoard) {
        var b2 Board

        b2 = v.Interface().(Board) // cast the type interface{} into type Board
        fmt.Printf("v converted back to: %#v\n", b2)
    } else {
        fmt.Printf("t is not recognized")
    }

}

Notice that the type of v is main.Board, the full package name, not Board. Any structure you want to do this to, must have an exported type for reflection to work.

AndrewN
  • 701
  • 3
  • 10
  • Great this solves most of my problem. So now I can pass a `reflect.Value` into my `DoTheThing` function. But how in `DoTheThing` would I then get turn the passed `reflect.Value` back into the underlying `*MyRandomComplicatedStructType` that it knows to expect? – Nat Sep 13 '14 at 18:27