Як відсортувати Map [рядок] 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повернення неправильного результату. Для зворотного сортування використовуйте >.
Fred Foo

3
@larsmans Мій поганий! Дякуємо, що вказали на це. Натомість я використав sort.Reverse, щоб отримати зворотні результати
samol

2
Навіть краще, я навіть не знав про це sort.Reverse. +1.
Fred Foo

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


Мені не подобається, як результат не є картою, з якої я почав
Гендрі

@hendry ця відповідь конкретно відповідає формату оригінального запитання. У go1.12 ви можете просто роздрукувати карту, і вона буде відсортована, див. Проблему: github.com/golang/go/issues/21095
voutasaurus,

майже ідеально, я пропоную обробляти елементи з однаковим значенням.
Томмазо Барбульї

@TommasoBarbugli як? Зробити їх стабільними? Або відсортовані за алфавітом? Досить впевнений, що стабільність неможлива, тому що Go рандомізує порядок ітерацій карти, щоб зупинити сподівання на порядок, який явно називається несуттєвим для компілятора. Для алфавіту ви можете легко змінити анонімну функцію, передану сортуванню. Зріз
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

@DarshanComputing: Дякую, виправлено.
zzzz

1
Це передбачає, що значення не мають ідентичності.
newacct

2
@newacct: Він вирішує лише проблему OP, а не загальний випадок ;-)
zzzz

це рішення, яке працювало і в моєму випадку, просте для розуміння.
Томмі

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

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
Я впевнений, хто голосує проти, можливо, ті, хто не читає компаратор уважно. Також прийнята відповідь не дає запиту на роздруківку, натомість запроваджує нову структуру даних, яка повинна підтримуватися в реальному коді. Ось посилання на майданчик для моєї відповіді play.golang.org/p/Y4lrEm2-hT5
Олександр
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.