FuriousGeorge
FuriousGeorge

Reputation: 4681

Join map keys in golang

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 a map[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()
}

Upvotes: 7

Views: 21431

Answers (1)

Paul Hankin
Paul Hankin

Reputation: 58271

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.

Upvotes: 19

Related Questions