Вложенные карты в Голанге

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? Или нет поддержки нескольких кронштейнов для вложенных карт?

Ответ 1

Нулевое значение для типов карт равно nil. Он еще не инициализирован. Вы не можете хранить значения на карте nil, что приводит к панике в процессе выполнения.

В последнем примере вы инициализируете (внешнюю) карту data, но у нее нет записей. Когда вы индексируете его как data["a"], поскольку в нем еще нет записи с ключом "a", индексирование возвращает нулевое значение типа значения, которое равно nil для карт. Поэтому попытка присвоения data["a"]["w"] является паникой времени выполнения.

Вы должны сначала инициализировать карту, прежде чем хранить в ней элементы, например:

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

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

Обратите внимание, что когда вы объявляете переменную типа карты и инициализируете ее составным литералом (как в var data = map[string]string{}), это также считается инициализацией.

Обратите внимание, что вы также можете инициализировать свои вложенные карты с помощью составного литерала:

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.

Ответ 2

В дополнение к icza ответ. Инициализацию карты можно записать в краткой форме:

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)

Результат такой же. Попробуйте на игровой площадке Go. Ключ "d" добавлен, чтобы продемонстрировать отображение с пустой картой.

Ответ 3

Хотя самый простой ответ на этот вопрос заключается в инициализации вложенных карт, как описано ранее, существует другой потенциальный вариант, зависящий от вашего шаблона доступа. Если вам нужна действительно иерархическая система карт, то предыдущие ответы просто хороши. Однако, если вам просто нужно посмотреть значения на карте, используя несколько аспектов, продолжайте читать!

Для карт вполне допустимо использовать структуры в качестве ключей (фактически может использоваться все, что сопоставимо). Таким образом, вы можете использовать одну карту со структурными ключами, как в этом примере, из блога Golang, который представляет собой счетчик посещений, который отслеживает попадания страниц по странам:

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"}]

Я не вижу таких карт достаточно часто, вместо этого многие люди сначала ищут вложенный вариант, который, я думаю, не всегда подходит.