Как отсортировать map по порядку, указанному в массиве ключей?
Есть map
, а точнее map[string]int
По умолчанию, как я понял, Golang
сортирует элементы по ключу в алфавитном порядке. А как отсортировать map
по ключам в заданном порядке?
Этот код:
package main
import (
"fmt"
)
func main() {
output := make(map[string]int)
basket := map[string]int{
"orange": 5,
"apple": 7,
"mango": 3,
"strawberry": 9,
}
//нужный порядок
keys := []string{
"strawberry",
"mango",
"orange",
}
for _, k := range keys {
output[k] = basket[k]
}
fmt.Printf("вывод output : %+v \n", output)
}
выдаст с ключами строки в алфавитном порядке:
mango, orange, strawberry
Ответы (2 шт):
map
- это несортированная коллекция вида ключ:значение, порядок вставки не сохраняется, перебор делается по сортированным ключам (но это не точно). Примерно также ведёт себя обычный словарь в Питоне (до какой-то версии, кажется 3.6) и в других языках программирования.
Чтобы сохранить порядок вставки нужно писать либо свой класс, либо использовать готовый пакет с сохранением порядка вставки orderedmap.
Для печати map
стандартная библиотека действительно сортирует по ключам. Это делает функция fmtsort.Sort
Но сам перебор ключей/значений в Go умышленно рандомизирован. Каждый раз, как вы итерируете ключи в мапе, может получаться новая последовательность: https://go.dev/play/p/wgHyoYTGC-t
package main
import "fmt"
func main() {
basket := map[string]int{
"orange": 5,
"apple": 7,
"mango": 3,
"strawberry": 9,
"mellon": 1,
"pineapple": 4,
"papaya": 1,
}
for i := 0; i < 5; i++ {
fmt.Println("Попытка №", i)
for k := range basket {
fmt.Printf(" ключ: %+v \n", k)
}
}
}
Попытка № 0
ключ: apple
ключ: mango
ключ: strawberry
ключ: mellon
ключ: pineapple
ключ: papaya
ключ: orange
Попытка № 1
ключ: orange
ключ: apple
ключ: mango
ключ: strawberry
ключ: mellon
ключ: pineapple
ключ: papaya
Попытка № 2
ключ: strawberry
ключ: mellon
ключ: pineapple
ключ: papaya
ключ: orange
ключ: apple
ключ: mango
.
.
.
Для сортировки мапы нужно создать массив пар ключ-значение и сортировать его: https://go.dev/play/p/8SbeIbCTleJ
package main
import (
"fmt"
"slices"
"strings"
)
type Pair[K any, V any] struct {
Key K
Value V
}
type PairList[K any, V any] []Pair[K, V]
type PairComparator[K any, V any] func(Pair[K, V], Pair[K, V]) int
func SortMap[K comparable, V any](m map[K]V, cf PairComparator[K, V]) PairList[K, V] {
result := make(PairList[K, V], len(m))
i := 0
for k, v := range m {
result[i] = Pair[K, V]{k, v}
i += 1
}
slices.SortFunc(result, func(a Pair[K, V], b Pair[K, V]) int { return cf(a, b) })
return result
}
func SortMapKeys[K comparable, V any](m map[K]V, cf PairComparator[K, V]) []K {
result := make([]K, len(m))
i := 0
for k := range m {
result[i] = k
i += 1
}
slices.SortFunc(result, func(a K, b K) int { return cf(Pair[K, V]{a, m[a]}, Pair[K, V]{b, m[b]}) })
return result
}
func main() {
basket := map[string]int{
"orange": 5,
"apple": 7,
"mango": 3,
"strawberry": 9,
"mellon": 1,
"pineapple": 4,
"papaya": 1,
}
sortedBasket := SortMap(basket, basketSorter)
for _, pair := range sortedBasket {
fmt.Println("sorted basket: ", pair.Key, pair.Value)
}
}
func basketSorter(p1, p2 Pair[string, int]) int {
if p1.Value < p2.Value {
return -1
}
if p1.Value > p2.Value {
return 1
}
return strings.Compare(p1.Key, p2.Key)
}
Результат:
sorted basket: mellon 1
sorted basket: papaya 1
sorted basket: mango 3
sorted basket: pineapple 4
sorted basket: orange 5
sorted basket: apple 7
sorted basket: strawberry 9