Jak posortować Map [string] int według jego wartości?

81

Biorąc pod uwagę ten blok kodu

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

Chciałbym wydrukować

foo, 20
bar, 20
hello, 10

W kolejności od najwyższego do najniższego

Dzięki!

samol
źródło

Odpowiedzi:

92

Znalazłem odpowiedź na temat orzechów Golang autorstwa Andrew Gerranda

Możesz zaimplementować interfejs sortowania, pisząc funkcje len / less / swap

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

Oryginalny post znajdziesz tutaj: https://groups.google.com/forum/#!topic/golang-nuts/FT7cjmcL7gw

samol
źródło
1
... z wyjątkiem Lesszwracania złego wyniku. Do sortowania wstecznego użyj >.
Fred Foo
3
@larsmans My bad! Dzięki za wskazanie tego. Zamiast tego użyłem sort.Reverse, aby uzyskać odwrotne wyniki
samol
2
Nawet lepiej, nawet o tym nie wiedziałem sort.Reverse. +1.
Fred Foo
72

Jest nowa funkcja sort.Slice w go 1.8, więc teraz jest to prostsze.

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
źródło
Nie podoba mi się, że wynik nie jest mapą, od której zacząłem
hendry
@hendry ta odpowiedź jest odpowiedzią na format w oryginalnym pytaniu. W go1.12 możesz po prostu wydrukować mapę, a zostanie ona posortowana, zobacz numer: github.com/golang/go/issues/21095
voutasaurus
prawie idealne, proponuję uchwyt do elementów o tej samej wartości.
Tommaso Barbugli
@TommasoBarbugli jak? Uczynić je stabilnymi? Albo posortowane alfabetycznie? Całkiem pewna stabilność jest niemożliwa, ponieważ Go losuje kolejność iteracji mapy specjalnie po to, aby nie polegać na kolejności, która jest jawnie wywoływana jako nieistotna dla kompilatora. W przypadku alfabetycznej można łatwo zmodyfikować anonimową funkcję przekazaną do sort.Slice
voutasaurus
1
Istnieje również sort.SliceStable (również dodany w Go 1.8), który zachowuje oryginalną kolejność równych elementów.
Dave Yarwood
16

Na przykład:

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

Plac zabaw


Wynik:

foo, 20
bar, 20
hello, 10
zzzz
źródło
@DarshanComputing: Dzięki, naprawiono.
zzzz,
1
Zakłada się, że wartości nie mają tożsamości.
newacct
2
@newacct: Rozwiązuje tylko problem OP, a nie przypadek ogólny ;-)
zzzz
to rozwiązanie działało również w moim przypadku, łatwe do zrozumienia.
Tommy
1

Często muszę sortować map[string]intcoś, co liczę i używam następujących.

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
}

Użyj go do iteracji po kluczach w kolejności według wartości

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
źródło
0

W moim przypadku miałem do czynienia z programem, który stworzyłem. W tym programie stworzyłem Mapę taką jak Ty, używając stringiint . Potem odkryłem, tak jak ty, że Go tak naprawdę nie ma wbudowanego sposobu na uporządkowanie czegoś takiego. Przeczytałem inne odpowiedzi i nie podobało mi się to, co przeczytałem.

Więc spróbowałem inaczej pomyśleć o problemie. Go może użyć sort.Ints z plasterkiem. Go może również używać sort.Slice z niestandardowym komparatorem. Więc zamiast tworzyć mapę stringi int, utworzyłem structof stringi int. Następnie możesz sortować:

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

To nie zadziała dla wszystkich, bo być może będziesz zmuszony zająć się mapą stworzoną przez kogoś innego. Ale to było dla mnie przydatne. Dobra część jest taka, że ​​w przeciwieństwie do wszystkich innych odpowiedzi ta nie używa pętli.

Steven Penny
źródło
-1

Sortuj klucze najpierw według wartości, a następnie iteruj mapę:

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
źródło
1
Jestem pewien, kto głosuje w dół, prawdopodobnie ci, którzy nie czytają uważnie funkcji porównawczych. Przyjęta również odpowiedź nie daje wydruku OP, o który prosiliśmy, ale zamiast tego wprowadza nową strukturę danych, która musi być utrzymywana w rzeczywistym kodzie. Oto link do placu zabaw dla mojej odpowiedzi play.golang.org/p/Y4lrEm2-hT5
AlexanderYastrebov