【问题标题】:Is there an easy way to iterate over a map in order?有没有一种简单的方法可以按顺序遍历地图?
【发布时间】:2013-06-15 07:36:36
【问题描述】:

这是古老的“为什么我的地图打印不正常”问题的变体。

我有(相当大)数量的map[MyKey]MyValue 形式的映射,其中MyKey 和MyValue(通常)是结构。我对所有键类型都有“更少”的功能。

我需要按顺序遍历地图。 (具体来说,该类型的 less 函数定义的顺序。)现在,我的代码如下所示:

type PairKeyValue struct {
    MyKey
    MyValue
}

type PairKeyValueSlice []Pair

func (ps PairKeyValueSlice) Len() int {
    return len(ps)
}

func (ps PairKeyValueSlice) Swap(i,j int) {
    ps[i], ps[j] = ps[j], ps[i]
}

func (ps PairKeyValueSlice) Less(i,j int) {
    return LessKey(ps[i].MyKey, ps[j].MyKey)
}

func NewPairKeyValueSlice(m map[MyKey]MyValue) (ps PairKeyValueSlice) {
    ps = make(PairKeyValueSlice, len(m))
    i := 0
    for k,v := range m {
        ps[i] = PairKeyValue{k,v}
        i++
    }
    sort.Sort(ps)
}

然后,每当我想要按顺序迭代时,它看起来像:

var m map[MyKey]MyValue
m = GetMapFromSomewhereUseful()
for _, kv := range NewPairKeyValueSlice(m) {
    key := kv.MyKey
    value := kv.MyValue
    DoUsefulWork(key, value)
}

这似乎在很大程度上有效。问题是它非常冗长。特别是因为手头的问题实际上与实现有序映射几乎没有关系,而是真正与循环中的有用工作有关。

另外,我有几种不同的键和值类型。因此,每次我想按顺序遍历地图时,我都会复制/粘贴所有代码,并使用新键查找/替换 MyKey 并使用新值替换 MyValue。复制/粘贴到那个量级是……“臭”。这已经很麻烦了,因为我已经犯了一些错误,我不得不多次修复。

这种技术也有缺点,它需要制作所有键和值的完整副本。这是不可取的,但我看不到解决方法。 (我可以将其简化为键,但不会改变问题的主要性质。)

This question 正在尝试用字符串做同样的事情。 This question 使用字符串和整数。 This question 意味着您需要使用反射,并且必须有一个 switch 语句来切换所有可能的类型,包括所有用户定义的类型。

但是对于地图不能确定性迭代感到困惑的人,似乎必须有更好的解决方案来解决这个问题。我来自 OO 背景,所以我可能缺少一些基本的东西。

那么,有没有一种合理的方法可以按顺序遍历地图?


更新:编辑问题以获得有关来源的更多信息,以防有比这更好的解决方案。

我有很多东西需要分组输出。每个分组级别的结构如下所示:

type ObjTypeTree struct {
    Children map[Type]*ObjKindTree
    TotalCount uint
}
type ObjKindTree struct {
    Children map[Kind]*ObjAreaTree
    TotalCount uint
}
type ObjAreaTree struct {
    Children map[Area]*ObjAreaTree
    TotalCount uint
    Objs []*Obj
}

然后,我将遍历 ObjTypeTree 中的子项以打印类型分组。对于其中的每一个,我都会遍历 ObjKindTree 以打印 Kind 分组。迭代是通过类型上的方法完成的,每种类型都需要稍微不同的方式来打印其分组级别。需要按顺序打印组,这会导致问题。

【问题讨论】:

  • 你已经告诉了我们你真正糟糕的解决方案。告诉我们您要解决什么问题,以便我们提出好的解决方案。
  • 好的,我已经编辑了一些关于我要解决的问题的信息。

标签: go


【解决方案1】:

如果需要密钥整理,请不要使用地图。使用 B 树或任何其他/类似的有序容器。

【讨论】:

  • 这是一个想法,但我不会有同样的复制/粘贴问题,我为它可能包含的每种类型重新实现相同的有序容器一次吗?可能有一些方法可以使用 interface{} 来破坏类型安全,但这有它自己的问题。
  • some libraries you can use 已经为您解决了这个问题。 :) 其中之一是使用 gotgo,一些 - 或多或少 - 为您提供伪泛型的预处理器。所以,如果你喜欢冒险......
  • Hrm,它看起来越来越像我必须击败类型系统才能做到这一点。 (或者使用预处理器;我不知道哪个更糟。)我希望得到不同的答案。
  • 我在 Go 中遇到困难或“感觉不对”时的经历,我使用了错误的数据结构或算法。
【解决方案2】:

我第二个 jnml 的答案。但是如果你想要比你拥有的更短的东西并且愿意放弃编译时类型安全,那么my library 可能对你有用。 (它建立在reflect 之上。)这是一个完整的工作示例:

package main

import (
    "fmt"

    "github.com/BurntSushi/ty/fun"
)

type OrderedKey struct {
    L1 rune
    L2 rune
}

func (k1 OrderedKey) Less(k2 OrderedKey) bool {
    return k1.L1 < k2.L1 || (k1.L1 == k2.L1 && k1.L2 < k2.L2)
}

func main() {
    m := map[OrderedKey]string{
        OrderedKey{'b', 'a'}: "second",
        OrderedKey{'x', 'y'}: "fourth",
        OrderedKey{'x', 'x'}: "third",
        OrderedKey{'a', 'b'}: "first",
        OrderedKey{'x', 'z'}: "fifth",
    }

    for k, v := range m {
        fmt.Printf("(%c, %c): %s\n", k.L1, k.L2, v)
    }
    fmt.Println("-----------------------------")

    keys := fun.QuickSort(OrderedKey.Less, fun.Keys(m)).([]OrderedKey)
    for _, k := range keys {
        v := m[k]
        fmt.Printf("(%c, %c): %s\n", k.L1, k.L2, v)
    }
}

注意这样的方法会比较慢,所以如果你需要性能,这不是一个好的选择。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-01
    • 1970-01-01
    • 2011-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-06
    相关资源
    最近更新 更多