在Golang中嵌套映射(Nested maps)

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

Nested maps in Golang

问题

func main() {
var data = map[string]string{}
data["a"] = "x"
data["b"] = "x"
data["c"] = "x"
fmt.Println(data)
}

它可以运行。

func main() {
var data = map[string][]string{}
data["a"] = append(data["a"], "x")
data["b"] = append(data["b"], "x")
data["c"] = append(data["c"], "x")
fmt.Println(data)
}

它也可以运行。

func main() {
var w = map[string]string{}
var data = map[string]map[string]string{}
w["w"] = "x"
data["a"] = w
data["b"] = w
data["c"] = w
fmt.Println(data)
}

它再次可以运行!

func main() {
var data = map[string]map[string]string{}
data["a"]["w"] = "x"
data["b"]["w"] = "x"
data["c"]["w"] = "x"
fmt.Println(data)
}

但它失败了!?

在Go语言中,嵌套映射存在问题吗?或者说嵌套映射不支持多重括号?

英文:
func main() {
	var data = map[string]string{}
	data["a"] = "x"
	data["b"] = "x"
	data["c"] = "x"
	fmt.Println(data)
}

It runs.

func main() {
	var data = map[string][]string{}
	data["a"] = append(data["a"], "x")
	data["b"] = append(data["b"], "x")
	data["c"] = append(data["c"], "x")
	fmt.Println(data)
}

It also runs.

func main() {
	var w = map[string]string{}
	var data = map[string]map[string]string{}
	w["w"] = "x"
	data["a"] = w
	data["b"] = w
	data["c"] = w
	fmt.Println(data)
}

It runs again!

func main() {
	var data = map[string]map[string]string{}
	data["a"]["w"] = "x"
	data["b"]["w"] = "x"
	data["c"]["w"] = "x"
	fmt.Println(data)
}

But it fails!?

Is there a problem with nested maps in Go? Or is there no multiple bracket support for nested maps?

答案1

得分: 103

map类型的零值是nil,它尚未初始化。你不能在nil的map中存储值,否则会导致运行时错误。

在你的最后一个示例中,你初始化了(外部)data map,但它没有任何条目。当你像data["a"]这样索引它时,由于它尚未包含键为"a"的条目,索引操作会返回值类型的零值,对于map来说就是nil。因此,试图对data["a"]["w"]进行赋值会导致运行时错误。

在存储元素之前,你必须先初始化一个map,例如:

var data = map[string]map[string]string{}

data["a"] = map[string]string{}
data["b"] = make(map[string]string)
data["c"] = make(map[string]string)

data["a"]["w"] = "x"
data["b"]["w"] = "x"
data["c"]["w"] = "x"
fmt.Println(data)

输出结果(在Go Playground上尝试):

map[a:map[w:x] b:map[w:x] c:map[w:x]]

请注意,当你声明一个map类型的变量并使用复合字面量进行初始化(如var data = map[string]string{}),这也算作初始化操作。

另外,你也可以使用复合字面量初始化嵌套的map:

var data = map[string]map[string]string{
	"a": map[string]string{},
	"b": map[string]string{},
	"c": map[string]string{},
}

data["a"]["w"] = "x"
data["b"]["w"] = "x"
data["c"]["w"] = "x"
fmt.Println(data)

输出结果相同。在Go Playground上尝试一下吧。

英文:

The zero value for map types is nil. It is not yet initialized. You cannot store values in a nil map, that's a runtime panic.

In your last example you initialize the (outer) data map, but it has no entries. When you index it like data["a"], since there is no entry with "a" key in it yet, indexing it returns the zero value of the value type which is nil for maps. So attempting to assign to data["a"]["w"] is a runtime panic.

You have to initialize a map first before storing elements in it, for example:

var data = map[string]map[string]string{}

data["a"] = map[string]string{}
data["b"] = make(map[string]string)
data["c"] = make(map[string]string)

data["a"]["w"] = "x"
data["b"]["w"] = "x"
data["c"]["w"] = "x"
fmt.Println(data)

Output (try it on the Go Playground):

map[a:map[w:x] b:map[w:x] c:map[w:x]]

Note that when you declare a variable of map type and initialize it with a composite literal (as in var data = map[string]string{}), that also counts as initializing.

Note that you may also initialize your nested maps with a composite literal:

var data = map[string]map[string]string{
	"a": map[string]string{},
	"b": map[string]string{},
	"c": map[string]string{},
}

data["a"]["w"] = "x"
data["b"]["w"] = "x"
data["c"]["w"] = "x"
fmt.Println(data)

Output is the same. Try it on the Go Playground.

答案2

得分: 17

尽管最直接的答案是按照之前描述的方法初始化嵌套的映射,但根据你的访问模式,还有另一种潜在的选择。如果你需要一个真正的层次结构映射系统,那么之前的答案就可以了。然而,如果你只是需要使用多个要素在映射中查找值,那么请继续阅读!

映射完全可以使用结构体作为键(实际上,任何可比较的内容都可以用作键)。因此,你可以使用一个带有结构体键的单个映射,就像 Golang 博客中的这个示例一样,它是一个按国家统计页面点击次数的计数器:

type Key struct {
  Path, Country string
}

hits := make(map[Key]int)

// 设置:越南人访问主页
hits[Key{"/", "vn"}]++

// 获取:查看有多少中国人阅读了规范
n := hits[Key{"/ref/spec", "cn"}]

我并不经常看到这样的映射用法,相反,很多人首先使用嵌套的变体,但我认为这并不总是合适的选择。

英文:

While the most straightforward answer to this question is to initialize your nested maps as previously described, there is another potential option depending on your access pattern. If you need a truly hierarchical system of maps, then the previous answers are just fine. However, if you simply need to look up values in the map using multiple facets, read on!

It is totally acceptable for maps to use structs as keys (in fact, anything that is comparable can be used). Thus, you can use a single map with struct keys like this example from the Golang blog, which is a hit counter that tracks page hits by country:

type Key struct {
  Path, Country string
}

hits := make(map[Key]int)

// set: Vietnamese person visiting the home page
hits[Key{"/", "vn"}]++

// get: see how many Chinese persons read the spec
n := hits[Key{"/ref/spec", "cn"}]

I don't see maps like this often enough, instead many people reach for the nested variant first, which I think may not always be the right fit.

答案3

得分: 12

除了icza的答案之外,地图初始化可以用简写形式来编写:

var data = map[string]map[string]string{
    "a": {"w": "x"},
    "b": {"w": "x"},
    "c": {"w": "x"},
    "d": {},
}
fmt.Println(data)

输出结果是相同的。在Go Playground上尝试一下。添加键"d"是为了演示与空地图的映射。

英文:

In addition to icza's answer. Map initialization can be written in short form:

var data = map[string]map[string]string{
	"a": map[string]string{
		"w": "x"},
	"b": map[string]string{
		"w": "x"},
	"c": map[string]string{
		"w": "x"},
	"d": map[string]string{},
}
fmt.Println(data)

Output is the same. Try it on the Go Playground. The key "d" added to demonstrate the mapping with an empty map.

答案4

得分: 2

以下解决方案可能对你有用。

var data = map[string]interface{}{
    "publishInfo": map[string]interface{}{
        "title":       publishInfo.Title,
        "description": publishInfo.Desc,
        "thumbnail":   publishInfo.ImageSrc,
        "url":         publishInfo.URL,
        "tags":        publishInfo.Tags,
    },
    "revision": draftInfo.Revision,
}
英文:

The below solution might be useful for you.

var data = map[string]interface{}{
		"publishInfo": map[string]interface{}{
			"title":       publishInfo.Title,
			"description": publishInfo.Desc,
			"thumbnail":   publishInfo.ImageSrc,
			"url":         publishInfo.URL,
			"tags":        publishInfo.Tags,
		},
		"revision": draftInfo.Revision,
	}

huangapple
  • 本文由 发表于 2017年6月1日 18:53:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/44305617.html
匿名

发表评论

匿名网友

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

确定