Join map keys in golang
Most of the time, I'd just write the obvious code:
func KeysString(m map[string]bool) string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return "[" + strings.Join(keys, ", ") + "]"
}
If you need efficiency more than readability, you can look at the implementation of strings.Join
for an idea on how to write this minimising copies. The main difference between this and your code is that a []byte
of exactly the right length is constructed which prevents the data being copied around when the buffer has to resize as the result is getting built up.
func KeysString(m map[string]bool) string {
if len(m) == 0 {
return "[]"
}
n := 2 * len(m) // (len-1) commas (", "), and one each of "[" and "]".
for k := range m {
n += len(k)
}
b := make([]byte, n)
bp := copy(b, "[")
first := true
for k := range m {
if !first {
bp += copy(b[bp:], ", ")
}
bp += copy(b[bp:], k)
first = false
}
bp += copy(b[bp:], "]")
return string(b)
}
Of course, be sure to profile and optimise in the context of the code you're using this function to make sure the readability tradeoff is actually worth it.
Related videos on Youtube
FuriousGeorge
Updated on September 14, 2022Comments
-
FuriousGeorge over 1 year
I want to join all of the keys of a map into a single string of the form [k1, k2, ...]. I'm not overly concerned with the order, just that I can make the string. I know that there is the function
strings.Join()
but it takes in a[]string
and not amap[string]bool
.I want to do this in the most efficient/fastest way possible (i.e. I don't want to create an entire copy of the keys just so I can slice over it). I couldn't find a way to just get a slice of the map's keys, so I came up with the following function instead. Obviously it's not the greatest because it does an unnecessary write and trunc.
Is there a way to just slice over the map keys?
func CreateStringArray(myMap map[string]bool) string { if myMap == nil || len(myMap) == 0 { return "[ ]" } buf := bytes.NewBufferString("[") for k, _ := range myMap { buf.WriteString(k) buf.WriteString(", ") } buf.Truncate(buf.Len() - 2) buf.WriteString("]") return buf.String() }
-
Linear over 9 yearsHonestly, I'd just use a normal "+=" and downslice unless profiling proved it was a bottleneck.
-
FuriousGeorge over 9 yearsYah, it's just odd that there's no m.Keys() to be able to provide to the strings.Join, especially given that you can
range
iterate over the map. -
siritinga over 9 yearsGo doesn't have many syntactic sugars. It can be done in four lines.
-
Ehsan Kia over 5 yearsWell, this merged with the fact that Golang doesn't have real sets. this means that something as simple as '|'.join(set(list)) in python becomes: create map[string]struct{}, loop to fill it up, create []string, loop to fill it up, call strings.Join. So one line turned into two loops and 10 lines.