Как отсортировать Map [string] int по ее значениям?

81

Учитывая этот блок кода

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

Я хотел бы распечатать

foo, 20
bar, 20
hello, 10

В порядке от самого высокого до самого низкого

Благодаря!

Самол
источник

Ответы:

92

Нашел ответ на голанг-орехах Эндрю Герранда

Вы можете реализовать интерфейс сортировки, написав функции 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] }

Исходный пост можно найти здесь https://groups.google.com/forum/#!topic/golang-nuts/FT7cjmcL7gw.

Самол
источник
1
... кроме того, Lessчто возвращает неверный результат. Для обратной сортировки используйте >.
Фред Фу
3
@larsmans Моя плохая! Спасибо, что указали на это. Вместо этого я использовал sort.Reverse, чтобы получить обратные результаты
samol
2
Более того, я даже не знал об этом sort.Reverse. +1.
Фред Фу
72

В go 1.8 есть новая функция sort.Slice, так что теперь это проще.

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

вутазавр
источник
Мне не нравится, что на выходе получается не та карта, с которой я начал
Хендри 01
@hendry этот ответ является ответом на формат исходного вопроса. В go1.12 вы можете просто распечатать карту, и она будет отсортирована, см.
Проблему
почти идеально, я предлагаю обрабатывать элементы с одинаковым значением.
Томмазо Барбугли
@TommasoBarbugli как? Сделать их стабильными? Или по алфавиту? Совершенно очевидно, что стабильность невозможна, потому что Go рандомизирует порядок итераций карты специально, чтобы вы не полагались на порядок, который явно вызывается как несущественный для компилятора. Для алфавитного вы можете легко изменить анонимную функцию, переданную в sort.Slice
voutasaurus
1
Также существует sort.SliceStable (также добавленный в Go 1.8), который сохраняет исходный порядок равных элементов.
Дэйв Ярвуд,
16

Например:

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

Игровая площадка


Вывод:

foo, 20
bar, 20
hello, 10
zzzz
источник
@DarshanComputing: Спасибо, исправлено.
zzzz
1
Это предполагает, что у значений нет идентичности.
newacct
2
@newacct: Он решает только проблему OP, а не общий случай ;-)
zzzz
это решение, которое сработало и в моем случае, простое для понимания.
Tommy
1

Мне часто нужно отсортировать map[string]intто, что я считаю, и я использовал следующее.

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
}

Используйте его для перебора ключей в порядке значений

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
источник
0

В моем случае я имел дело с программой, которую создал. В этой программе я создал карту, как и вы, с помощью stringи int. Затем я, как и вы, обнаружил, что в Go нет встроенного способа отсортировать что-то подобное. Я прочитал другие ответы, и мне не очень понравилось то, что я прочитал.

Поэтому я попытался по-другому взглянуть на проблему. В Go можно использовать sort.Ints с срезом. Кроме того, Go может использовать sort.Slice с настраиваемым компаратором. Поэтому вместо создания карты из stringи intя создал structиз stringи int. Затем вы можете отсортировать:

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

Это не сработает для всех, потому что, возможно, вам придется иметь дело с картой, созданной кем-то другим. Но мне это пригодилось. Хорошая часть заключается в том, что, в отличие от всех других ответов, в этом нет циклов.

Стивен Пенни
источник
-1

Сначала отсортируйте ключи по значению, а затем выполните итерацию карты:

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])
    }
}
Александр Ястребов
источник
1
Я очень уверен, кто голосует против, возможно, тех, кто не очень внимательно читает функции компаратора. Также принятый ответ не создает запрошенную OP распечатки, а вместо этого вводит новую структуру данных, которая должна поддерживаться в реальном коде. Вот ссылка на мой ответ play.golang.org/p/Y4lrEm2-hT5
Александр