Как получить срез значений из map в Go?
Сбор значений из map в срез
В Go нет встроенного метода для получения всех значений map в виде среза, но это делается очень просто с помощью цикла range. Вот самый стандартный и понятный способ:
package main
import "fmt"
func main() {
scores := map[string]int{
"Alice": 95,
"Bob": 87,
"Charlie": 92,
}
// Шаг 1: создаём срез с начальной ёмкостью, равной длине map
values := make([]int, 0, len(scores))
// Шаг 2: проходим по map и добавляем каждое значение в срез
for _, v := range scores {
values = append(values, v)
}
fmt.Println("Все оценки:", values) // [95 87 92] (порядок может отличаться)
}
Почему это работает?
Цикл for _, v := range scores итерирует по всем парам ключ-значение в map. Мы игнорируем ключ (используя _) и сохраняем только значение v. Функция append добавляет каждое значение в срез. Использование make([]int, 0, len(scores)) задаёт начальную ёмкость, что позволяет избежать лишних переаллокаций памяти и повышает производительность.
Важно: порядок значений не гарантирован
В Go map — это неупорядоченная структура данных. Порядок обхода пар в цикле range не определён и может меняться от запуска к запуску. Если вам нужен фиксированный порядок значений, придётся собирать и ключи, а затем сортировать:
import "sort"
// Получаем отсортированные значения по ключам
keys := make([]string, 0, len(scores))
for k := range scores {
keys = append(keys, k)
}
sort.Strings(keys)
sortedValues := make([]int, 0, len(scores))
for _, k := range keys {
sortedValues = append(sortedValues, scores[k])
}
fmt.Println("Отсортированные оценки:", sortedValues) // [87 92 95]
Универсальная функция для любых map
Если вам часто нужно получать значения из map, можно написать небольшую обобщённую функцию с использованием дженериков (доступны с Go 1.18):
func MapValues[K comparable, V any](m map[K]V) []V {
values := make([]V, 0, len(m))
for _, v := range m {
values = append(values, v)
}
return values
}
// Использование:
values := MapValues(scores) // []int{95, 87, 92}
Эта функция работает с любым типом map: map[string]int, map[int]string, map[string]struct{} и так далее.
Заключение
Самый простой и эффективный способ — цикл range с append. Помните про недетерминированный порядок и используйте сортировку, если он важен. Для повторного использования создайте обобщённую функцию.
Альтернативный подход: предварительное выделение и индексация
Вместо append можно создать срез с полной длиной и заполнять его по индексу:
values := make([]int, len(scores))
i := 0
for _, v := range scores {
values[i] = v
i++
}
fmt.Println(values)
Этот способ может быть немного быстрее в некоторых случаях, так как избегает вызовов append, но разница обычно незначительна. Главное — не забыть про переменную-счётчик i.
Когда это полезно?
- Если вы точно знаете, что значения map не будут меняться во время итерации.
- Если вам нужно строго контролировать выделение памяти (например, в высоконагруженных системах).
Оба подхода корректны, но вариант с append считается более идиоматичным для Go и проще читается. Выбирайте тот, который вам удобнее.