Home > OS >  Using "dynamic" key to extract value from map
Using "dynamic" key to extract value from map

Time:06-06

Came from javascript background, and just started with Golang. I am learning all the new terms in Golang, and creating new question because I cannot find the answer I need (probably due to lack of knowledge of terms to search for)

I created a custom type, created an array of types, and I want to create a function where I can retrieve all the values of a specific key, and return an array of all the values (brands in this example)

type Car struct {
  brand string
  units int
}

....

var cars []Car
var singleCar Car

//So i have a loop here and inside the for-loop, i create many single cars
singleCar = Car {
   brand: "Mercedes",
   units: 20
}

//and i append the singleCar into cars
cars = append(cars, singleCar)

Now what I want to do is to create a function that I can retrieve all the brands, and I tried doing the following. I intend to have key as a dynamic value, so I can search by specific key, e.g. brand, model, capacity etc.

func getUniqueByKey(v []Car, key string) []string {
    var combined []string

    for i := range v {
        combined = append(combined, v[i][key]) 
        //this line returns error -
        //invalid operation: cannot index v[i] (map index expression of type Car)compilerNonIndexableOperand
    }
    return combined
    //This is suppose to return ["Mercedes", "Honda", "Ferrari"]
}

The above function is suppose to work if i use getUniqueByKey(cars, "brand") where in this example, brand is the key. But I do not know the syntaxes so it's returning error.

CodePudding user response:

Seems like you're trying to get a property using a slice accessor, which doesn't work in Go. You'd need to write a function for each property. Here's an example with the brands:

func getUniqueBrands(v []Car) []string {
    var combined []string
    tempMap := make(map[string]bool)

    for _, c := range v {
        if _, p := tempMap[c.brand]; !p {
            tempMap[c.brand] = true
            combined = append(combined, c.brand)
        }
    }
    return combined
}

Also, note the for loop being used to get the value of Car here. Go's range can be used to iterate over just indices or both indices and values. The index is discarded by assigning to _.

I would recommend re-using this code with an added switch-case block to get the result you want. If you need to return multiple types, use interface{} and type assertion.

CodePudding user response:

Maybe you could marshal your struct into json data then convert it to a map. Example code:

package main

import (
    "encoding/json"
    "fmt"
)

type RandomStruct struct {
    FieldA string
    FieldB int
    FieldC string
    RandomFieldD bool
    RandomFieldE interface{}
}

func main() {
    fieldName := "FieldC"
    randomStruct := RandomStruct{
        FieldA:       "a",
        FieldB:       5,
        FieldC:       "c",
        RandomFieldD: false,
        RandomFieldE: map[string]string{"innerFieldA": "??"},
    }
    randomStructs := make([]RandomStruct, 0)
    randomStructs = append(randomStructs, randomStruct, randomStruct, randomStruct)
    res := FetchRandomFieldAndConcat(randomStructs, fieldName)
    fmt.Println(res)
}

func FetchRandomFieldAndConcat(randomStructs []RandomStruct, fieldName string) []interface{} {
    res := make([]interface{}, 0)
    for _, randomStruct := range randomStructs {
        jsonData, _ := json.Marshal(randomStruct)
        jsonMap := make(map[string]interface{})
        err := json.Unmarshal(jsonData, &jsonMap)
        if err != nil {
            fmt.Println(err)
            // panic(err)
        }
        value, exists := jsonMap[fieldName]
        if exists {
            res = append(res, value)
        }
    }
    return res
}
  •  Tags:  
  • go
  • Related