Как перебрать карту в Голанге по порядку?

Пожалуйста, смотрите ниже мою карту

var romanNumeralDict map[int]string = map[int]string{
  1000: "M",
  900 : "CM",
  500 : "D",
  400 : "CD",
  100 : "C",
  90  : "XC",
  50  : "L",
  40  : "XL",
  10  : "X",
  9   : "IX",
  5   : "V",
  4   : "IV",
  1   : "I",
}

Я ищу, чтобы просмотреть эту карту в порядке размера ключа

  for k, v := range romanNumeralDict {
    fmt.Println("k:", k, "v:", v)
  }

Тем не менее, это распечатывает

k: 1000 v: M
k: 40 v: XL
k: 5 v: V
k: 4 v: IV
k: 900 v: CM
k: 500 v: D
k: 400 v: CD
k: 100 v: C
k: 90 v: XC
k: 50 v: L
k: 10 v: X
k: 9 v: IX
k: 1 v: I

Есть ли способ, которым я могу распечатать их в порядке размера ключа, поэтому я хотел бы просмотреть эту карту, как это

k:1
K:4
K:5
K:9
k:10

так далее...

Большое спасибо за Вашу помощь!

9 ответов

Решение

Соберите все ключи, отсортируйте их и итерируйте карту по ключу, как показано ниже:

keys := make([]int, 0)
for k, _ := range romanNumeralDict {
    keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
    fmt.Println(k, romanNumeralDict[k])
}

Вы можете сделать это немного быстрее, предварительно выделив keys потому что вы знаете его длину:

func sortedKeys(m map[Key]Value) ([]Key) {
        keys := make([]Key, len(m))
        i := 0
        for k := range m {
            keys[i] = k
            i++
        }
        sort.Keys(keys)
        return keys
}

замещать Key а также Value с вашим ключом и типами значений (включая sort линия). кашель дженерики кашель

Если скорость выполнения не является большой проблемой, вы можете получить отсортированный массив ключей, используя MapKeys.

В этом примере ключи имеют тип string:

keys := reflect.ValueOf(myMap).MapKeys()
keysOrder := func(i, j int) bool { return keys[i].Interface().(string) < keys[j].Interface().(string) }
sort.Slice(keys, keysOrder)

// process map in key-sorted order
for _, key := range keys {
    value := myMap[key.Interface().(string)]
    fmt.Println(key, value)
}
  • См.: Получение фрагмента ключей с карты
  • Предупреждение: это обходит некоторую безопасность типов во время компиляции (паника, если не карта)
  • Вам нужно будет привести каждый ключ, чтобы получить его необработанное значение: key.Interface().(string)

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

Вместо этого вы могли бы использовать срез:

      var romanNumeralDict = []string{
    1000: "M",
    900:  "CM",
    500:  "D",
    400:  "CD",
    100:  "C",
    90:   "XC",
    50:   "L",
    40:   "XL",
    10:   "X",
    9:    "IX",
    5:    "V",
    4:    "IV",
    1:    "I",
}
for k, v := range romanNumeralDict {
    if v == "" {
        continue
    }
    fmt.Println("k:", k, "v:", v)
}

Бегите по детской площадке .

Примечание. Этот код требует немного больше места и времени, но вы можете видеть, что код стандартной библиотеки Go использует тот же шаблон во многих местах.

Вы можете перебирать карту по порядку, сначала явно отсортировав ключи, а затем перебирая карту по ключу. Поскольку вы знаете окончательный размер ключей с самого начала romanNumeralDict, более эффективно заранее выделить массив необходимого размера.

// Slice for specifying the order of the map.
// It is initially empty but has sufficient capacity
// to hold all the keys of the romanNumeralDict map.
keys := make([]int, 0, len(romanNumeralDict))

// Collect keys of the map
i := 0
for k, _ := range romanNumeralDict {
    keys[i] = k
    i++
}

// Ints sorts a slice of ints in increasing order
sort.Ints(keys)

// Iterate over the map by key with an order
for _, k := range keys {
    fmt.Println(k, romanNumeralDict[k])
}

Основываясь на ответе @Brent, у меня был случай, когда я хотел отсортировать ключи карты в некритическом фрагменте кода, не повторяясь слишком много. Итак, вот отправная точка для создания общей функции итерации карты для многих разных типов:

      func sortedMapIteration(m interface{}, f interface{}) {
    // get value and keys
    val := reflect.ValueOf(m)
    keys := val.MapKeys()
    var sortFunc func(i, j int) bool
    kTyp := val.Type().Key()

    // determine which sorting function to use for the keys based on their types.
    switch {
    case kTyp.Kind() == reflect.Int:
        sortFunc = func(i, j int) bool { return keys[i].Int() < keys[j].Int() }
    case kTyp.Kind() == reflect.String:
        sortFunc = func(i, j int) bool { return keys[i].String() < keys[j].String() }
    }
    sort.Slice(keys, sortFunc)

    // get the function and call it for each key.
    fVal := reflect.ValueOf(f)
    for _, key := range keys {
        value := val.MapIndex(key)
        fVal.Call([]reflect.Value{key, value})
    }
}

// example:
func main() {
    sortedMapIteration(map[string]int{
        "009": 9,
        "003": 3,
        "910": 910,
    }, func(s string, v int) {
        fmt.Println(s, v)
    })
}

playground

Подчеркнем: этот код неэффективен и использует отражение , поэтому он не обеспечивает безопасности типов во время компиляции, а универсальная реализация должна иметь больше гарантий типов и обрабатывать больше типов ключей. Однако для быстрых и грязных сценариев это может помочь вам начать работу. Вам нужно будет добавить больше вариантов в блок switch, в зависимости от того, какие типы ключей вы ожидаете передать.

Краткая версия с Go 1.21:

      m := make(map[string]string)
// fill the map
keys := maps.Keys(m)
slices.Sort(keys)
for k := range kyes {
    fmt.Println(m[k])
}

Просто распечатайте карту. Все ответы здесь теперь содержат старое поведение карт. В Go 1.12+ вы можете просто напечатать значение карты, и оно будет автоматически отсортировано по ключу. Это было добавлено, потому что это позволяет легко тестировать значения карт.

func main() {
    m := map[int]int{3: 5, 2: 4, 1: 3}
    fmt.Println(m)

    // In Go 1.12+
    // Output: map[1:3 2:4 3:5]

    // Before Go 1.12 (the order was undefined)
    // map[3:5 2:4 1:3]
}

Карты теперь печатаются в порядке сортировки ключей, чтобы упростить тестирование. Правила заказа:

  • Когда это применимо, ноль сравнивается с низким
  • порядок чисел, чисел с плавающей точкой и строк по <
  • NaN сравнивает меньше, чем не-NaN
  • bool сравнивает false перед true
  • Комплекс сравнивает реальное, то и воображаемое
  • Указатели сравниваются по машинному адресу
  • Значения каналов сравниваются по машинному адресу
  • Структуры сравнивают каждое поле по очереди
  • Массивы сравнивают каждый элемент по очереди
  • Значения интерфейса сначала сравниваются по отражению. Тип, описывающий конкретный тип, а затем по конкретному значению, как описано в предыдущих правилах.

При печати карт значения неотражающих ключей, такие как NaN, ранее отображались как. Начиная с этого выпуска, правильные значения печатаются.

Узнайте больше здесь.

Вы также можете использовать этот пакет https://github.com/wk8/go-ordered-map. Использование производительности/памяти пакета должно быть ограничено, но, похоже, оно отвечает потребностям.

Он работает как карта, но его можно повторять следующим образом:

      for pair := om.Oldest(); pair != nil; pair = pair.Next() {
        fmt.Printf("%d => %s\n", pair.Key, pair.Value.payload)
}
Другие вопросы по тегам