Під час ітерації через повернуту карту в коді, повернену функцією теми, клавіші відображаються не в порядку.
Як я можу привести ключі в порядок / відсортувати карту так, щоб ключі були в порядку, а значення відповідали?
Ось код .
Під час ітерації через повернуту карту в коді, повернену функцією теми, клавіші відображаються не в порядку.
Як я можу привести ключі в порядок / відсортувати карту так, щоб ключі були в порядку, а значення відповідали?
Ось код .
Відповіді:
Перейти до блогу: Go карти в дії має чудове пояснення.
При ітерації по карті з циклом діапазону порядок ітерацій не вказується і не гарантується однаковість від однієї ітерації до наступної. Оскільки Go 1 час виконання рандомізує порядок ітерацій карти, оскільки програмісти покладалися на стабільний порядок ітерацій попередньої реалізації. Якщо вам потрібен стабільний порядок ітерацій, ви повинні підтримувати окрему структуру даних, яка визначає цей порядок.
Ось моя змінена версія прикладу коду: http://play.golang.org/p/dvqcGPYy3-
package main
import (
"fmt"
"sort"
)
func main() {
// To create a map as input
m := make(map[int]string)
m[1] = "a"
m[2] = "c"
m[0] = "b"
// To store the keys in slice in sorted order
keys := make([]int, len(m))
i := 0
for k := range m {
keys[i] = k
i++
}
sort.Ints(keys)
// To perform the opertion you want
for _, k := range keys {
fmt.Println("Key:", k, "Value:", m[k])
}
}
Вихід:
Key: 0 Value: b
Key: 1 Value: a
Key: 2 Value: c
keys := make([]int, len(m))
а потім вставити за індексом keys[i] = k
замістьappend
Відповідно до специфікації Go , порядок ітерацій на карті не визначений і може різнитися залежно від запуску програми. На практиці він не тільки не визначений, він насправді навмисно рандомізований. Це тому, що раніше це було передбачувано, і розробники мови Go не хотіли, щоб люди покладались на невизначену поведінку, тому вони навмисно рандомізували це так, що покладатися на цю поведінку було неможливо.
Тоді вам потрібно буде втягнути клавіші в зріз, відсортувати їх, а потім перенести по зрізу так:
var m map[keyType]valueType
keys := sliceOfKeys(m) // you'll have to implement this
for _, k := range keys {
v := m[k]
// k is the key and v is the value; do your computation here
}
Всі відповіді тут містять стару поведінку карт. У 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]
}
Карти тепер друкуються в порядку сортування ключів, щоб полегшити тестування. Правила впорядкування:
- За необхідності, нуль порівняно з низьким
- ints, floats та strings упорядкувати за <
- NaN порівнює менше, ніж не-NaN
- bool порівнює false перед true
- Комплекс порівнює реальні, то уявні
- Покажчики порівнюють за адресою машини
- Порівняння значень каналів за адресою машини
- Структури по черзі порівнюють кожне поле
- Масиви порівнюють кожен елемент по черзі
- Значення інтерфейсу порівнюються спочатку за допомогою відображення. Тип, що описує конкретний тип, а потім за конкретним значенням, як описано в попередніх правилах.
Під час друку карт раніше невідбивні значення ключів, такі як NaN, раніше відображалися як
<nil>
. Станом на цей випуск друкуються правильні значення.
Детальніше читайте тут .
Якщо, як і я, ви виявите, що хочете, по суті, один і той же код сортування більш ніж в одному місці або просто хочете зменшити складність коду, ви можете абстрагувати саму сортування до окремої функції, якій ви передаєте функцію, яка робить фактичну роботу, яку ви хочете (яка, звичайно, буде різною на кожному сайті виклику).
Враховуючи карту з типом ключа K
та типом значення V
, представленими як <K>
і <V>
нижче, загальна функція сортування може виглядати приблизно так, як цей шаблон Go-code (який Go версія 1 не підтримує як є):
/* Go apparently doesn't support/allow 'interface{}' as the value (or
/* key) of a map such that any arbitrary type can be substituted at
/* run time, so several of these nearly-identical functions might be
/* needed for different key/value type combinations. */
func sortedMap<K><T>(m map[<K>]<V>, f func(k <K>, v <V>)) {
var keys []<K>
for k, _ := range m {
keys = append(keys, k)
}
sort.Strings(keys) # or sort.Ints(keys), sort.Sort(...), etc., per <K>
for _, k := range keys {
v := m[k]
f(k, v)
}
}
Потім зателефонуйте йому з картою введення та функцією (беручи (k <K>, v <V>)
за аргументи введення), яка викликається над елементами карти в порядку сортування ключів.
Отже, версія коду у відповіді, опублікованій Mingu, може виглядати так:
package main
import (
"fmt"
"sort"
)
func sortedMapIntString(m map[int]string, f func(k int, v string)) {
var keys []int
for k, _ := range m {
keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
f(k, m[k])
}
}
func main() {
// Create a map for processing
m := make(map[int]string)
m[1] = "a"
m[2] = "c"
m[0] = "b"
sortedMapIntString(m,
func(k int, v string) { fmt.Println("Key:", k, "Value:", v) })
}
sortedMapIntString()
Функція може бути повторно використана для будь-якогоmap[int]string
(припускаючи , що той же порядок сортування по бажанню), зберігаючи кожне використання до двох рядків коду.
Мінуси включають:
Інші мови мають різні рішення:
<K>
і<V>
(для позначення типів ключа та значення) виглядає дещо звичним, цей шаблон коду не страшно відрізняється від шаблонів С ++.range
першокласний тип таким, що його можна було б замінити користувацьким ordered-range
(замість range
оригінального коду), я думаю, що деякі інші мови надають ітератори, які є досить потужними, щоб виконати те саме річ.У відповідь на Джеймса Крейга Берлі відповідь . Для того, щоб зробити чистий і багаторазовий дизайн, можна вибрати більш об’єктно-орієнтований підхід. Таким чином методи можна безпечно прив'язати до типів зазначеної карти. Для мене такий підхід здається чистішим та організованішим.
Приклад:
package main
import (
"fmt"
"sort"
)
type myIntMap map[int]string
func (m myIntMap) sort() (index []int) {
for k, _ := range m {
index = append(index, k)
}
sort.Ints(index)
return
}
func main() {
m := myIntMap{
1: "one",
11: "eleven",
3: "three",
}
for _, k := range m.sort() {
fmt.Println(m[k])
}
}
Приклад розширеного дитячого майданчика з декількома типами карт.
У всіх випадках карта та відсортований фрагмент роз’єднуються з моменту закінчення for
циклу над картою range
. Це означає, що якщо карта змінюється після логіки сортування, але перед тим, як використовувати її, ви можете потрапити в неприємності. (Не обробляти потоки / переходити в безпечний режим). Якщо відбувається зміна паралельного доступу до запису Map, вам потрібно буде використовувати мьютекс навколо записів та відсортованого for
циклу.
mutex.Lock()
for _, k := range m.sort() {
fmt.Println(m[k])
}
mutex.Unlock()
Це надає вам приклад коду на сортувальній карті. В основному це те, що вони пропонують:
var keys []int
for k := range myMap {
keys = append(keys, k)
}
sort.Ints(keys)
// Benchmark1-8 2863149 374 ns/op 152 B/op 5 allocs/op
і ось що я б запропонував використовувати замість цього :
keys := make([]int, 0, len(myMap))
for k := range myMap {
keys = append(keys, k)
}
sort.Ints(keys)
// Benchmark2-8 5320446 230 ns/op 80 B/op 2 allocs/op
Повний код можна знайти на цьому майданчику Go .