如果在Go语言中无法对地图进行排序,正确的方法是什么?

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

If maps can't be sorted in Go lang, what is the right way to do it?

问题

假设我有一个字符串,我想要统计每个字母的频率,并按频率对表进行排序。对于输入的字符串"hello larry",期望的输出如下:

+--------+-----------+
| Letter | Occurence |
+--------+-----------+
| l      |         3 |
| r      |         2 |
| h      |         1 |
| e      |         1 |
| o      |         1 |
| a      |         1 |
| y      |         1 |
+--------+-----------+

起初,我想使用map来处理,将字母作为键。这非常简单。然而,map的项没有顺序,因此无法排序。

我猜可以使用一个结构体来解决这个问题:

type Letter struct {
    Value string
    Score int
}
type LetterList []Letter

然而,这会带来一系列其他问题:

  1. 我需要检查Letter是否已经存在于LetterList中,因为我不能使用字母作为键。
  2. 没有直接的方法对它们进行排序(例如使用Int.sort())。

使用这些结构体感觉上并不优雅。有更好的解决方案吗?

英文:

Let's say I have a string and I would like to count each letter's frequency and then sort the table by the frequency. Desired output of "hello larry" would be:

+--------+-----------+
| Letter | Occurence |
+--------+-----------+
| l      |         3 |
| r      |         2 |
| h      |         1 |
| e      |         1 |
| o      |         1 |
| a      |         1 |
| y      |         1 |
+--------+-----------+

First I thought I'll deal with this easily using map having the letters as keys. This is really easy. However, map items don't have an order hence can't be sorted.

I guess I could deal with this using a structure:

type Letter struct {
    Value string
    Score int
}
type LetterList []Letter

However that brings bunch of other problems:

  1. I need to check if the Letter is not already present in LetterList because I can't use the letters as keys
  2. There's no direct way to sort them (using Int.sort() or so)

Using the structures just doesn't feel elegant at all. Is there a better solution?

答案1

得分: 3

你会惊讶于在一个小切片上进行循环的速度和效率是多么快,而且你可以相对简单地在其上实现排序。

我建议阅读http://golang.org/pkg/sort/ SortWrapper。

type Letter struct {
    Value rune
    Score int
}

type LetterList []*Letter

func (ll *LetterList) FindOrAdd(r rune) (l *Letter) {
    for _, l = range *ll {
        if l.Value == r {
            return
        }
    }
    l = &Letter{Value: r, Score: 0}
    *ll = append(*ll, l)
    return
}

func (ll LetterList) SortByScore() LetterList {
    sort.Sort(llByScore{ll})
    return ll
}

func (ll LetterList) SortByValue() LetterList {
    sort.Sort(llByValue{ll})
    return ll
}

func (ll LetterList) String() string {
    var b bytes.Buffer
    b.WriteByte('[')
    for _, v := range ll {
        b.WriteString(fmt.Sprintf("{%q, %d}, ", v.Value, v.Score))
    }
    b.WriteByte(']')
    return b.String()

}

func New(s string) (ll LetterList) {
    ll = LetterList{}
    for _, r := range s {
        ll.FindOrAdd(r).Score++
    }
    return
}

func (ll LetterList) Len() int      { return len(ll) }
func (ll LetterList) Swap(i, j int) { ll[i], ll[j] = ll[j], ll[i] }

type llByScore struct{ LetterList }

func (l llByScore) Less(i, j int) bool {
    return l.LetterList[i].Score > l.LetterList[j].Score
}

type llByValue struct{ LetterList }

func (l llByValue) Less(i, j int) bool {
    return l.LetterList[i].Value > l.LetterList[j].Value
}

func main() {
    ll := New(`Let's say I have a string and I would like to count each letter's frequency and then sort the table by the frequency. Desired output of "hello larry" would be`)
    fmt.Println(ll)
    fmt.Println(ll.SortByScore())
    fmt.Println(ll.SortByValue())
}

另一种方法是使用映射,然后为了排序,将其转换为列表并进行排序。

英文:

You would be surprised how fast and efficient looping over a small slice is, and you can implement sorting on top of it fairly simple.

I recommend reading http://golang.org/pkg/sort/ SortWrapper.

type Letter struct {
Value rune
Score int
}
type LetterList []*Letter
func (ll *LetterList) FindOrAdd(r rune) (l *Letter) {
for _, l = range *ll {
if l.Value == r {
return
}
}
l = &Letter{Value: r, Score: 0}
*ll = append(*ll, l)
return
}
func (ll LetterList) SortByScore() LetterList {
sort.Sort(llByScore{ll})
return ll
}
func (ll LetterList) SortByValue() LetterList {
sort.Sort(llByValue{ll})
return ll
}
func (ll LetterList) String() string {
var b bytes.Buffer
b.WriteByte('[')
for _, v := range ll {
b.WriteString(fmt.Sprintf("{%q, %d}, ", v.Value, v.Score))
}
b.WriteByte(']')
return b.String()
}
func New(s string) (ll LetterList) {
ll = LetterList{}
for _, r := range s {
ll.FindOrAdd(r).Score++
}
return
}
func (ll LetterList) Len() int      { return len(ll) }
func (ll LetterList) Swap(i, j int) { ll[i], ll[j] = ll[j], ll[i] }
type llByScore struct{ LetterList }
func (l llByScore) Less(i, j int) bool {
return l.LetterList[i].Score > l.LetterList[j].Score
}
type llByValue struct{ LetterList }
func (l llByValue) Less(i, j int) bool {
return l.LetterList[i].Value > l.LetterList[j].Value
}
func main() {
ll := New(`Let's say I have a string and I would like to count each letter's frequency and then sort the table by the frequency. Desired output of "hello larry" would be`)
fmt.Println(ll)
fmt.Println(ll.SortByScore())
fmt.Println(ll.SortByValue())
}

<kbd>playground</kbd>

Another approach is to use a map then for sorting generate a list out of it and sort it.

huangapple
  • 本文由 发表于 2014年8月17日 23:34:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/25350710.html
匿名

发表评论

匿名网友

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

确定