I am new to Go and now I want to get an arbitrary item from a map; what\'s the idiomatic way to do that? I can only think of something like this:
func get_some_k
In my case, the map only had one key which I needed to extract so for that you can do:
var key string
var val string
for k, v := range myMap {
key = k
val = v
break
}
For multiple keys you could do something like,
func split_map(myMap map[string]string, idx int) (string[], string[]) {
keys := make([]string, len(myMap))
values := make([]string, len(myMap))
count := 0
for k, v := range myMap {
keys[count] = k
values[count] = v
count = count + 1
}
return keys, values
}
While for accessing ith element,
func get_ith(myMap map[string]string, idx int) (string, string) {
count := 0
for k, v := range myMap {
if idx == count {
return k, v
}
count = count + 1
}
return "", ""
}
It is usually not a good idea to force an API on a data-structure that doesn't intrinsically support it. At best it will be slow, hacky, hard-to-test, hard-to-debug and unstable. Go's map
natively supports upsert
, get
, delete
, and length
but not GetRandom
.
Of the two concrete solutions mentioned here
The other solutions talk about using additional data structures to help the map support this operation. This is what I think makes the most sense
type RandomizedSet interface {
Delete(key int) // O(1)
Get(key int) int // O(1)
GetRandomKey() int // O(1)
Len() int // O(1)
Upsert(key int, val int) // O(1)
}
type randomizedset struct {
h map[int]int // map key to its index in the slice
indexes []int // each index in the slice contains the value
source rand.Source // rng for testability, seeding, and distribution
}
func New(source rand.Source) RandomizedSet {
return &randomizedset{
h: make(map[int]int, 0),
indexes: make([]int, 0),
source: source,
}
}
// helper to accomodate Delete operation
func (r *randomizedset) swap(i, j int) {
r.indexes[i], r.indexes[j] = r.indexes[j], r.indexes[i]
r.h[r.indexes[i]] = i
r.h[r.indexes[j]] = j
}
// remainder of implementations here
As a "global" solution, as I am a big fan of elasticsearch, you could use another map/array to store your data, to build a kind of an inverted dictionary.
Maybe what you want is a array, which is easy to access randomly, especially the container is random read heavy but changed infrequently.
Whether getting an arbitrary key from a hash table is a common requirement may be discussed. Other language map implementations often lack this feature (eg. Dictionary in C# )
However, your solution is probably the fastest one, but you will be left with a pseudo-random algorithm that you do not control. And while the current implementation uses a pseudo-random algorithm, the Go Specification doesn't give you any assurance it will actually be random, only that it is not guaranteed to be predictable:
The iteration order over maps is not specified and is not guaranteed to be the same from one iteration to the next.
If you want more control of the randomization, you can also in parallel keep an updated slice of values (or keys) contained in the map, using the randomization of your choice (math/rand
or crypto/rand
for more extreme cases) to get the value stored at an index, selected randomly, in the slice.
Here is a more generic version, although it may be less efficient:
keys := reflect.ValueOf(mapI).MapKeys()
return keys[rand.Intn(len(keys))].Interface()
https://play.golang.org/p/0uvpJ0diG4e