-2

How do I iterate over the map of interfaces below to get the values of the interface returned in the map?

I have read through the extensive list of questions regarding iteration in Go but they didn't help me.

// https://api.kraken.com/0/public/AssetPairs

pairsResult, err := api.Query("AssetPairs", map[string]string{
})

if err != nil {
    log.Fatal(err)
}

ks := reflect.ValueOf(pairsResult).MapKeys()

fmt.Printf(" %+v ", pairsResult) // result A below
fmt.Printf(" %+v ", ks) // result B below

// the value here is the value of MapKeys, which is the key
for kix, key := range ks {
    fmt.Printf(" %+v %+v \n ", kix, key) // result C below
}

Result A

map[AAVEETH:map[aclass_base:currency aclass_quote:currency altname:AAVEETH base:AAVE fee_volume_currency:ZUSD fees:[[0 0.26] [50000 0.24] [100000 0.22] [250000 0.2] [500000 0.18]...

Result B

[KEEPXBT LINKUSD LINKXBT NANOEUR ...]

Result C

0 KEEPXBT 1 LINKUSD 2 LINKXBT 3 NANOEUR 4 USDTAUD ...

This is the source of the API wrapper function that is being called above

// Query sends a query to Kraken api for given method and parameters
func (api *KrakenAPI) Query(method string, data map[string]string) (interface{}, error) {
    values := url.Values{}
    for key, value := range data {
        values.Set(key, value)
    }

    // Check if method is public or private
    if isStringInSlice(method, publicMethods) {
        return api.queryPublic(method, values, nil)
    } else if isStringInSlice(method, privateMethods) {
        return api.queryPrivate(method, values, nil)
    }

    return nil, fmt.Errorf("Method '%s' is not valid", method)
}

This happens when I try to iterate over the value:

Iterate over value

This happens when I try to iterate over the initial result:

Iterate over result

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
dataphile
  • 344
  • 1
  • 5
  • 13

2 Answers2

1

Assuming you're using this and from looking a bit into its source it seems to me that the concrete type of the result will be map[string]interface{}, if that is the case then you can do this.

res, err := api.Query("AssetPairs", map[string]string{})
if err != nil {
    log.Fatal(err)
}

pairs, ok := res.(map[string]interface{})
if !ok {
    log.Fatal("unsupported type")
}

for k, v := range pairs {
    fmt.Printf("key=%s value=%+v\n ", k, v)
}
mkopriva
  • 35,176
  • 4
  • 57
  • 71
1

As the previous response mentions, we see that the interface returned becomes a map[string]interface{}, the following code would do the trick to retrieve the types:

    for _, v := range d.(map[string]interface{}) {
        switch v.(type) {
        case map[string]interface{}:
            fmt.Println("Its another map of string interface")
        case interface{}:
            fmt.Println("its an interface")
        case string:
            fmt.Println("its a string")
        case []string:
            fmt.Println("its a string array")
        case float32:
            fmt.Println("its a float32")
        case float64:
            fmt.Println("its a float64")
        default:
            fmt.Printf("Different thing, %T\n", v)
        }
    }

Code here: https://play.golang.org/p/81LLYSvJVf8

However, I would recommend use explicit type, that would make your life much easier:

// Generated by https://quicktype.io

type KrakenTypes struct {
    Error  []interface{}     `json:"error"` 
    Result map[string]Result `json:"result"`
}

type Result struct {
    Altname           string            `json:"altname"`            
    Wsname            *string           `json:"wsname,omitempty"`   
    AclassBase        Aclass            `json:"aclass_base"`        
    Base              string            `json:"base"`               
    AclassQuote       Aclass            `json:"aclass_quote"`       
    Quote             FeeVolumeCurrency `json:"quote"`              
    Lot               Lot               `json:"lot"`                
    PairDecimals      int64             `json:"pair_decimals"`      
    LotDecimals       int64             `json:"lot_decimals"`       
    LotMultiplier     int64             `json:"lot_multiplier"`     
    LeverageBuy       []int64           `json:"leverage_buy"`       
    LeverageSell      []int64           `json:"leverage_sell"`      
    Fees              [][]float64       `json:"fees"`               
    FeesMaker         [][]float64       `json:"fees_maker"`         
    FeeVolumeCurrency FeeVolumeCurrency `json:"fee_volume_currency"`
    MarginCall        int64             `json:"margin_call"`        
    MarginStop        int64             `json:"margin_stop"`        
    Ordermin          *string           `json:"ordermin,omitempty"` 
}

Here we can use json decoding once the response was read, so the iterations to find out the types of each level could be avoided and we can just access to each member directly.

Complete code here: https://play.golang.org/p/v3tlroyx1mW