Join map keys in golang

go
15,174

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.

Share:
15,174

Related videos on Youtube

FuriousGeorge
Author by

FuriousGeorge

Updated on September 14, 2022

Comments

  • FuriousGeorge
    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 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()
    }
    
  • Linear
    Linear over 9 years
    Honestly, I'd just use a normal "+=" and downslice unless profiling proved it was a bottleneck.
  • FuriousGeorge
    FuriousGeorge over 9 years
    Yah, 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
    siritinga over 9 years
    Go doesn't have many syntactic sugars. It can be done in four lines.
  • Ehsan Kia
    Ehsan Kia over 5 years
    Well, 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.