2013-06-18 35 views
4

这是一个古老的“为什么我的地图打印乱序”问题的变种。有没有简单的方法来遍历地图?

我有一个(相当大量)地图map[MyKey]MyValue,其中MyKeyMyValue是(通常)结构。我对所有关键类型都有“少”的功能。

我需要按顺序遍历地图。 (具体而言,为了通过该类型较少函数中定义)现在,我的代码如下所示:

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组。迭代是通过类型上的方法完成的,每种类型都需要一些打印分组级别的方法。组需要按顺序打印,这会导致问题。

+1

你已经告诉了我们关于你真正糟糕的解决方案的所有信息。告诉我们你想要解决什么问题,以便我们能够提出好的解决方案。 – peterSO

+0

好的,我已经编辑了一些关于我试图解决的问题的信息。 – alficles

回答

2

如果需要键校对,则不要使用映射。使用B型树或任何其他/类似订购容器。

+1

这是一个想法,但我不会只是有相同的复制/粘贴问题,我重新实现同一个有序的容器一次,它可以包含每种类型?可能有某种方法可以使用interface {}来打败类型安全,但这有它自己的问题。 – alficles

+2

有[一些](https://github.com/datastream/btree)[库](https://github.com/runningwild/go-btree)[你可以使用](https://github.com/stathat/treap)已经为你解决了这个问题。 :)其中之一是使用gotgo,一些 - 或多或少 - 预处理器,它给你伪泛型。所以,如果你感觉冒险... – nemo

+0

Hrm,它看起来越来越像我只能打败类型系统来做到这一点。 (或者使用预处理器;我不知道哪个更糟糕。)我希望得到不同的答案。 – alficles

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

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

相关问题