Wie sortiere ich eine Map [Zeichenfolge] int nach ihren Werten?

80

Angesichts dieses Codeblocks

map[string]int {"hello":10, "foo":20, "bar":20}

Ich möchte ausdrucken

foo, 20
bar, 20
hello, 10

In der Reihenfolge vom höchsten zum niedrigsten

Vielen Dank!

Samol
quelle

Antworten:

91

Fand die Antwort auf Golang-Nüsse von Andrew Gerrand

Sie können die Sortierschnittstelle implementieren, indem Sie die Funktionen len / less / swap schreiben

func rankByWordCount(wordFrequencies map[string]int) PairList{
  pl := make(PairList, len(wordFrequencies))
  i := 0
  for k, v := range wordFrequencies {
    pl[i] = Pair{k, v}
    i++
  }
  sort.Sort(sort.Reverse(pl))
  return pl
}

type Pair struct {
  Key string
  Value int
}

type PairList []Pair

func (p PairList) Len() int { return len(p) }
func (p PairList) Less(i, j int) bool { return p[i].Value < p[j].Value }
func (p PairList) Swap(i, j int){ p[i], p[j] = p[j], p[i] }

Den Originalbeitrag finden Sie hier https://groups.google.com/forum/#!topic/golang-nuts/FT7cjmcL7gw

Samol
quelle
1
... außer dass Lessdas falsche Ergebnis zurückgegeben wird. Verwenden Sie für die umgekehrte Sortierung >.
Fred Foo
3
@larsmans Mein schlechtes! Vielen Dank für den Hinweis. Ich habe stattdessen sort.Reverse verwendet, um die umgekehrten Ergebnisse zu erhalten
samol
2
Noch besser, ich wusste nicht einmal davon sort.Reverse. +1.
Fred Foo
71

Es gibt eine neue sort.Slice-Funktion in go 1.8, daher ist dies jetzt einfacher.

package main

import (
    "fmt"
    "sort"
)

func main() {
    m := map[string]int{
        "something": 10,
        "yo":        20,
        "blah":      20,
    }

    type kv struct {
        Key   string
        Value int
    }

    var ss []kv
    for k, v := range m {
        ss = append(ss, kv{k, v})
    }

    sort.Slice(ss, func(i, j int) bool {
        return ss[i].Value > ss[j].Value
    })

    for _, kv := range ss {
        fmt.Printf("%s, %d\n", kv.Key, kv.Value)
    }
}

https://play.golang.org/p/y1_WBENH4N

Voutasaurus
quelle
Ich mag es nicht, dass die Ausgabe nicht die Karte ist, mit der ich begonnen habe
Henry
@hendry Diese Antwort entspricht speziell dem Format in der ursprünglichen Frage. In go1.12 können Sie einfach die Karte drucken und sie wird sortiert, siehe Ausgabe: github.com/golang/go/issues/21095
voutasaurus
Fast perfekt, ich schlage vor, Elemente mit dem gleichen Wert zu handhaben.
Tommaso Barbugli
@TommasoBarbugli wie? Sie stabil machen? Oder alphabetisch sortiert? Ziemlich sicher, dass Stabilität nicht möglich ist, da Go die Reihenfolge der Karteniterationen randomisiert, um zu verhindern, dass Sie sich auf eine Reihenfolge verlassen, die für den Compiler ausdrücklich als nicht signifikant bezeichnet wird. In alphabetischer Reihenfolge können Sie die an sort
übergebene
1
Es gibt auch eine sort.SliceStable (ebenfalls in Go 1.8 hinzugefügt), die die ursprüngliche Reihenfolge der gleichen Elemente beibehält .
Dave Yarwood
16

Zum Beispiel:

package main

import (
        "fmt"
        "sort"
)

func main() {
        m := map[string]int{"hello": 10, "foo": 20, "bar": 20}
        n := map[int][]string{}
        var a []int
        for k, v := range m {
                n[v] = append(n[v], k)
        }
        for k := range n {
                a = append(a, k)
        }
        sort.Sort(sort.Reverse(sort.IntSlice(a)))
        for _, k := range a {
                for _, s := range n[k] {
                        fmt.Printf("%s, %d\n", s, k)
                }
        }
}

Spielplatz


Ausgabe:

foo, 20
bar, 20
hello, 10
zzzz
quelle
@DarshanComputing: Danke, behoben.
zzzz
1
Dies setzt voraus, dass die Werte keine Identitäten haben.
Newacct
2
@newacct: Es löst nur das OP-Problem, nicht den allgemeinen Fall ;-)
zzzz
Diese Lösung hat auch in meinem Fall funktioniert und ist einfach zu verstehen.
Tommy
1

Ich muss oft map[string]intetwas sortieren , das ich zähle, und habe Folgendes verwendet.

func rankMapStringInt(values map[string]int) []string {
    type kv struct {
        Key   string
        Value int
    }
    var ss []kv
    for k, v := range values {
        ss = append(ss, kv{k, v})
    }
    sort.Slice(ss, func(i, j int) bool {
        return ss[i].Value > ss[j].Value
    })
    ranked := make([]string, len(values))
    for i, kv := range ss {
        ranked[i] = kv.Key
    }
    return ranked
}

Verwenden Sie diese Option, um die Schlüssel in der Reihenfolge ihres Werts zu durchlaufen

values := map[string]int{"foo": 10, "bar": 20, "baz": 1}

for i, index := range rankMapStringInt(values) {
    fmt.Printf("%3d: %s -> %d", i, index, values[index])
}
Xeoncross
quelle
0

In meinem Fall hatte ich es mit einem Programm zu tun, das ich erstellt habe. In diesem Programm habe ich genau wie Sie eine Karte mit stringund erstellt int. Dann entdeckte ich wie Sie, dass Go nicht wirklich eine eingebaute Möglichkeit hat, so etwas zu sortieren. Ich las die anderen Antworten und mochte nicht wirklich, was ich las.

Also habe ich versucht, das Problem anders zu betrachten. Go kann sort.Ints mit einem Slice verwenden. Außerdem kann Go sort.Slice mit einem benutzerdefinierten Komparator verwenden. Anstatt eine Karte von stringund zu erstellen int, habe ich eine structvon stringund erstellt int. Dann können Sie sortieren:

package main

import (
   "fmt"
   "sort"
)

type File struct {
   Name string
   Size int
}

func main() {
   a := []File{{"april.txt", 9}, {"may.txt", 7}}
   f := func (n, n1 int) bool {
      return a[n].Size < a[n1].Size
   }
   sort.Slice(a, f)
   fmt.Println(a)
}

Dies funktioniert nicht für alle, da Sie möglicherweise gezwungen sind, sich mit einer Karte zu befassen, die von einer anderen Person erstellt wurde. Aber es war nützlich für mich. Der gute Teil ist, dass diese im Gegensatz zu allen anderen Antworten keine Schleifen verwendet.

Steven Penny
quelle
-2

Sortieren Sie die Schlüssel zuerst nach Wert und wiederholen Sie dann die Karte:

package main

import (
    "fmt"
    "sort"
)

func main() {
    counts := map[string]int{"hello": 10, "foo": 20, "bar": 20}

    keys := make([]string, 0, len(counts))
    for key := range counts {
        keys = append(keys, key)
    }
    sort.Slice(keys, func(i, j int) bool { return counts[keys[i]] > counts[keys[j]] })

    for _, key := range keys {
        fmt.Printf("%s, %d\n", key, counts[key])
    }
}
AlexanderYastrebov
quelle
Ich bin mir sehr sicher, wer abstimmt, wahrscheinlich diejenigen, die die Komparatorfunktion nicht sorgfältig lesen. Eine ebenfalls akzeptierte Antwort erzeugt nicht das angeforderte Ausdruck-OP, sondern führt stattdessen eine neue Datenstruktur ein, die in echtem Code gepflegt werden muss. Hier ist der Spielplatz-Link für meine Antwort play.golang.org/p/Y4lrEm2-hT5
AlexanderYastrebov