'How to write a function on a map with unknown type?
I read with interest a question about how to create a slice of the keys of a map in Go (using a range loop), but how would you write a function that would do this for a map of any type? The map value type is a particular nuisance since it doesn’t even affect the type of the resulting slice.
For instance, could I write a function that takes as argument a map[int]T (where T is any type) and returns a []int (integer slice) of the map’s keys? And if that’s possible, can a function be made to operate on a map[T1]T2 (where T1 and T2 are any type) and return a []T1 slice? That is, what do I replace the question marks with in the below code? I’m guessing I need an interface but I’m a bit of a beginner with those.
function keys(m map[?]?) []? {
var s []?
k := range m {
s = append(s, k)
}
return s
}
Solution 1:[1]
You could do it for types like map[int]interface{}.
func keys(m map[int]interface{}) []int {
var s []int
for k := range m {
s = append(s, k)
}
return s
}
To learn about interfaces, start with the Tour of Go: https://tour.golang.org/methods/9
But this is not possible with concrete types before Go gets generics.
Generics are on the way, though. Once they land in Go, you'll be able to write:
func keys[T any](m map[int]T) []int {
var s []int
for k := range m {
s = append(s, k)
}
return s
}
Solution 2:[2]
Use generics in Go 1.18 or later:
// Keys returns the keys from in map m.
func keys[M ~map[K]V, K comparable, V any](m M) []K {
result := make([]K, 0, len(m))
for k := range m {
result = append(result, k)
}
return result
}
Use it like this:
r := keys(map[string]string{"Foo": "Bar", "Quux": "Baz"})
// r is a []string
Use the reflect package to extract the keys from an arbitrary map type in Go versions before 1.18:
// Keys stores the keys from m to the slice pointed to by pkeys.
// The slice element type must be the same as the map key type.
func keys(m interface{}, pkeys interface{}) {
result := reflect.ValueOf(pkeys).Elem()
result.SetLen(0)
result.Set(reflect.Append(result, reflect.ValueOf(m).MapKeys()...))
}
Use it like this:
var result []string
keys(map[string]string{"Foo": "Bar", "Quux": "Baz"}, &result)
fmt.Println(result) // prints [Foo Quux]
Here's how to write the function for a specific key type. In this example, the key type is int:
// Keys returns the keys for for map m with int keys.
func keys(m interface{}) []int {
mv := reflect.ValueOf(m)
result := make([]int, mv.Len())
for i, key := range mv.MapKeys() {
result[i] = key.Interface().(int)
}
return result
}
Use it like this:
result := keys(map[int]string{1: "Bar", 2: "Baz"})
fmt.Println(result) // prints [1 2]
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
