在golang中合并map的键

7
我想将一个map的所有键合并为一个形如[k1,k2,...]的字符串。我不太关心顺序,只要能生成字符串就可以了。我知道有函数strings.Join(),但它需要[]string而不是map[string]bool。
我希望以最高效/最快的方式实现这一目标(即我不想创建整个键的副本,以便可以在其上切片)。我找不到仅获取map键的切片的方法,因此我想出了以下函数。显然,它并不是最好的,因为它做了不必要的写入和截断。
是否有办法直接在map键上进行切片?
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()
}
1个回答

19

大多数情况下,我只会写显而易见的代码:

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, ", ") + "]"
}

如果您更看重效率而非可读性,可以查看strings.Join的实现方式,以获得如何编写此类最小化复制的想法。它与您的代码之间的主要区别在于会构造一个恰好长度为[]byte的缓冲区,这可以避免在组装结果时需要调整缓冲区大小时数据被复制。

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)
}

当然,一定要在使用此函数的代码环境中进行分析和优化,以确保可读性权衡实际上是值得的。


1
说实话,除非性能测试证明这是一个瓶颈,否则我会使用普通的“+=”和“downslice”。 - Linear
是的,很奇怪没有m.Keys()可以提供给strings.Join,特别是考虑到你可以使用range迭代映射。 - FuriousGeorge
Go语言的语法糖并不多。这可以用四行代码完成。 - siritinga
2
这与Golang没有真正的集合有关。这意味着在Python中像'|'.join(set(list))这样简单的东西变成了:创建map[string]struct{},循环填充它,创建[]string,循环填充它,调用strings.Join。因此,一行代码变成了两个循环和10行代码。 - Ehsan Kia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接