如何在Go中创建一个不区分大小写的映射?

huangapple go评论87阅读模式
英文:

How to create a case insensitive map in Go?

问题

我想要一个不区分大小写的字符串作为键。这个语言是否支持,还是我需要自己创建呢?
谢谢

**编辑:**我想要的是一种默认的方法,而不是每次使用映射时都要记得转换键。

英文:

I want to have a key insensitive string as key.
Is it supported by the language or do I have to create it myself?
thank you

Edit: What I am looking for is a way to make it by default instead of having to remember to convert the keys every time I use the map.

答案1

得分: 12

编辑:我的初始代码实际上仍然允许使用map语法,从而允许绕过方法。这个版本更安全。

你可以“派生”一个类型。在Go中,我们只是声明。然后在你的类型上定义方法。只需要一个非常薄的包装器来提供你想要的功能。但请注意,你必须使用普通的方法调用语法来调用get和set。没有办法保留内置映射具有的索引语法或可选的ok结果。

package main

import (
    "fmt"
    "strings"
)

type ciMap struct {
    m map[string]bool
}

func newCiMap() ciMap {
    return ciMap{m: make(map[string]bool)}
}

func (m ciMap) set(s string, b bool) {
    m.m[strings.ToLower(s)] = b
}

func (m ciMap) get(s string) (b, ok bool) {
    b, ok = m.m[strings.ToLower(s)]
    return
}

func main() {
    m := newCiMap()
    m.set("key1", true)
    m.set("kEy1", false)
    k := "keY1"
    b, _ := m.get(k)
    fmt.Println(k, "value is", b)
}
英文:

Edit: My initial code actually still allowed map syntax and thus allowed the methods to be bypassed. This version is safer.

You can "derive" a type. In Go we just say declare. Then you define methods on your type. It just takes a very thin wrapper to provide the functionality you want. Note though, that you must call get and set with ordinary method call syntax. There is no way to keep the index syntax or optional ok result that built in maps have.

package main

import (
    "fmt"
    "strings"
)

type ciMap struct {
    m map[string]bool
}

func newCiMap() ciMap {
    return ciMap{m: make(map[string]bool)}
}

func (m ciMap) set(s string, b bool) {
    m.m[strings.ToLower(s)] = b
}

func (m ciMap) get(s string) (b, ok bool) {
    b, ok = m.m[strings.ToLower(s)]
    return
}

func main() {
    m := newCiMap()
    m.set("key1", true)
    m.set("kEy1", false)
    k := "keY1"
    b, _ := m.get(k)
    fmt.Println(k, "value is", b)
}

答案2

得分: 4

两种可能性:

  1. 如果您的输入集保证仅限于可以正确转换为大写/小写的字符(对于某些Unicode字符可能不成立),则将其转换为大写/小写。

  2. 否则,将其转换为Unicode折叠大小写:

使用unicode.SimpleFold(rune)将Unicode字符转换为折叠大小写。显然,这种操作比简单的ASCII风格的大小写映射要昂贵得多,但它在其他语言中也更具可移植性。请参阅EqualsFold的源代码以了解如何使用它,包括如何从源字符串中提取Unicode字符。

显然,您应该将此功能抽象为一个单独的包,而不是在每次使用映射时都重新实现它。这应该是不言而喻的,但你永远不知道。

英文:

Two possiblities:

  1. Convert to uppercase/lowercase if you're input set is guaranteed to be restricted to only characters for which a conversion to uppercase/lowercase will yield correct results (may not be true for some Unicode characters)

  2. Convert to Unicode fold case otherwise:

Use unicode.SimpleFold(rune) to convert a unicode rune to fold case. Obviously this is dramatically more expensive an operation than simple ASCII-style case mapping, but it is also more portable to other languages. See the source code for EqualsFold to see how this is used, including how to extract Unicode runes from your source string.

Obviously you'd abstract this functionality into a separate package instead of re-implementing it everywhere you use the map. This should go without saying, but then you never know.

答案3

得分: 1

这里有一个比仅使用strings.ToLower更强大的东西,你可以使用golang.org/x/text/cases包。示例:

package main
import "golang.org/x/text/cases"

func main() {
   s := cases.Fold().String("March")
   println(s == "march")
}

如果你想使用标准库中的东西,我运行了这个测试:

package main

import (
   "strings"
   "unicode"
)

func main() {
   var (
      lower, upper int
      m = make(map[string]bool)
   )
   for n := '\u0080'; n <= '\u07FF'; n++ {
      q, r := n, n
      for {
         q = unicode.SimpleFold(q)
         if q == n { break }
         for {
            r = unicode.SimpleFold(r)
            if r == n { break }
            s, t := string(q), string(r)
            if m[t + s] { continue }
            if strings.ToLower(s) == strings.ToLower(t) { lower++ }
            if strings.ToUpper(s) == strings.ToUpper(t) { upper++ }
            m
展开收缩
= true } } } println(lower == 951, upper == 989) }

因此,可以看出ToUpper是稍微更好的选择。

英文:

Here is something more robust than just strings.ToLower, you can use
the golang.org/x/text/cases package. Example:

package main
import &quot;golang.org/x/text/cases&quot;

func main() {
   s := cases.Fold().String(&quot;March&quot;)
   println(s == &quot;march&quot;)
}

If you want to use something from the standard library, I ran this test:

package main

import (
   &quot;strings&quot;
   &quot;unicode&quot;
)

func main() {
   var (
      lower, upper int
      m = make(map[string]bool)
   )
   for n := &#39;\u0080&#39;; n &lt;= &#39;\u07FF&#39;; n++ {
      q, r := n, n
      for {
         q = unicode.SimpleFold(q)
         if q == n { break }
         for {
            r = unicode.SimpleFold(r)
            if r == n { break }
            s, t := string(q), string(r)
            if m[t + s] { continue }
            if strings.ToLower(s) == strings.ToLower(t) { lower++ }
            if strings.ToUpper(s) == strings.ToUpper(t) { upper++ }
            m
展开收缩
= true } } } println(lower == 951, upper == 989) }

So as can be seen, ToUpper is the marginally better choice.

huangapple
  • 本文由 发表于 2012年6月21日 01:12:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/11124705.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定