How to iterate through a map in Golang in order?

huangapple go评论79阅读模式
英文:

How to iterate through a map in Golang in order?

问题

请参考下面的代码:

package main

import (
	"fmt"
	"sort"
)

func main() {
	romanNumeralDict := 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",
	}

	keys := make([]int, 0, len(romanNumeralDict))
	for k := range romanNumeralDict {
		keys = append(keys, k)
	}

	sort.Ints(keys)

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

这段代码会按照键的大小顺序打印出键和对应的值。

英文:

Please see below my map

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

I am looking to loop through this map in the order of the size of the key

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

However, this prints out

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

Is there a way that I can print them out in the order of the size of the key so, I would like to loop through this map like this

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

etc...

答案1

得分: 137

收集所有的键,对它们进行排序,并按键迭代你的映射,就像下面的代码一样:

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

请注意,这是一段Go语言的代码,用于收集、排序和迭代映射中的键和值。

英文:

Collect all keys, sort them and iterate your map by key, like the following:

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

答案2

得分: 44

你可以通过预先分配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
}

KeyValue替换为你的键和值类型(包括sort行)。咳咳,泛型咳咳

编辑:Go 1.18终于支持泛型了!这是泛型版本:

// Ordered 是一个类型约束,适用于任何有序类型。
// 有序类型是指支持 <、<=、>、>= 操作符的类型。
//
// 注意,泛型提案建议将来会从标准的 "constraints" 包中提供此类型。
type Ordered interface {
    type int, int8, int16, int32, int64,
        uint, uint8, uint16, uint32, uint64, uintptr,
        float32, float64,
        string
}

func sortedKeys[K Ordered, V any](m map[K]V) ([]K) {
    keys := make([]K, len(m))
    i := 0
    for k := range m {
        keys[i] = k
        i++
    }
    sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
    return keys
}

Playground 示例

英文:

You can make it a little faster by preallocating keys because you know its length:

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
}

Replace Key and Value with your key and value types (including the sort line). cough generics cough

Edit: Go 1.18 is finally getting generics! Here's the generic version:

// Ordered is a type constraint that matches any ordered type.
// An ordered type is one that supports the &lt;, &lt;=, &gt;, and &gt;= operators.
//
// Note the generics proposal suggests this type will be available from
// a standard &quot;constraints&quot; package in future.
type Ordered interface {
	type int, int8, int16, int32, int64,
		uint, uint8, uint16, uint32, uint64, uintptr,
		float32, float64,
		string
}

func sortedKeys[K Ordered, V any](m map[K]V) ([]K) {
        keys := make([]K, len(m))
        i := 0
        for k := range m {
            keys[i] = k
            i++
        }
        sort.Slice(keys, func(i, j int) bool { return keys[i] &lt; keys[j] })
        return keys
}

Playground example

答案3

得分: 2

你可以使用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)

// 按键排序的顺序处理映射
for _, key := range keys {
    value := myMap[key.Interface().(string)]
    fmt.Println(key, value)
}
  • 参考:https://stackoverflow.com/q/21362950/86967
  • 警告:这会绕过一些编译时的类型安全性(如果不是一个映射,则会引发恐慌)
  • 你需要将每个键强制转换为原始值才能获取它的值:key.Interface().(string)
英文:

You can get a sortable array of keys using MapKeys.

In this example, the keys are of type string:

keys := reflect.ValueOf(myMap).MapKeys()
keysOrder := func(i, j int) bool { return keys[i].Interface().(string) &lt; 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)
}

答案4

得分: 1

你可以通过先显式地对键进行排序,然后按键迭代地遍历map。由于你从一开始就知道了romanNumeralDict的键的最终大小,所以最好在一开始就分配一个所需大小的数组。

// 用于指定map顺序的切片。
// 它最初是空的,但具有足够的容量来容纳romanNumeralDict map的所有键。
keys := make([]int, 0, len(romanNumeralDict))

// 收集map的键
i := 0
for k, _ := range romanNumeralDict {
    keys[i] = k
    i++
}

// Ints函数按递增顺序对int切片进行排序
sort.Ints(keys)

// 按顺序按键迭代map
for _, k := range keys {
    fmt.Println(k, romanNumeralDict[k])
}

注意:以上代码是Go语言的示例代码,用于按顺序迭代map。

英文:

You can iterate over the map in order by sorting the keys explicitly first, and then iterate over the map by key. Since you know the final size of keys from the romanNumeralDict outset, it is more efficient to allocate an array of the required size up front.

// 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])
}

答案5

得分: 1

根据@Brent的回答,我有一个场景,我想在一个非关键的代码片段中对排序后的映射键进行操作,而不必重复太多代码。所以这里是一个起点,可以为许多不同类型创建一个通用的映射迭代函数:

func sortedMapIteration(m interface{}, f interface{}) {
    // 获取值和键
    val := reflect.ValueOf(m)
    keys := val.MapKeys()
    var sortFunc func(i, j int) bool
    kTyp := val.Type().Key()

    // 根据键的类型确定要使用的排序函数
    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)

    // 获取函数并对每个键调用它
    fVal := reflect.ValueOf(f)
    for _, key := range keys {
        value := val.MapIndex(key)
        fVal.Call([]reflect.Value{key, value})
    }
}

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

<kbd>playground</kbd>

强调一下:这段代码效率低下且使用了反射,因此它没有编译时类型安全性,通用实现应该有更多的类型保护并处理更多的键类型。然而,对于快速而简单的脚本,这可以帮助你入门。根据你期望传递的键类型,你需要在switch块中添加更多的情况。

英文:

Based on @Brent's answer, I had an occasion where I wanted sorted map keys in a non-critical piece of code, without having to repeat myself too much. So here is a starting point to make a generic map-iteration function for many different types:

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() &lt; keys[j].Int() }
	case kTyp.Kind() == reflect.String:
		sortFunc = func(i, j int) bool { return keys[i].String() &lt; 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{
		&quot;009&quot;: 9,
		&quot;003&quot;: 3,
		&quot;910&quot;: 910,
	}, func(s string, v int) {
		fmt.Println(s, v)
	})
}

<kbd>playground</kbd>

To stress: this code is inefficient and uses reflection, so it does not have compile-time type safety, and a generic implementation should have more type safeguards and handle more key types. However, for quick and dirty scripts this can help you get started. You will need to add more cases to the switch block, according to which key types you expect to pass.

答案6

得分: 0

你也可以使用这个包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)
}
英文:

You can also use this package https://github.com/wk8/go-ordered-map.
Performance/memory usage about the package needs to be bench but it seems to answer to the need.

It works like a map but can be iterated like this:

for pair := om.Oldest(); pair != nil; pair = pair.Next() {
		fmt.Printf(&quot;%d =&gt; %s\n&quot;, pair.Key, pair.Value.payload)
}

答案7

得分: 0

使用Go 1.21的简化版本:

m := make(map[string]string)
// 填充映射
keys := maps.Keys(m)
slices.Sort(keys)
for k := range kyes {
    fmt.Println(m[k])
}
英文:

Short version with 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])
}

huangapple
  • 本文由 发表于 2013年8月21日 02:47:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/18342784.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定