Sorting a Map of Structs - GOLANG - sorting

I have a map of structs that I am populating by streaming data to a Go program. The way the map is updated is similar to the example below.
Once I have this map of structs populated, what is the best (or good) way to sort this map by the values of the count field in the struct?
package main
type data struct {
count int64
}
func main() {
m := make(map[string]data)
m["x"] = data{0, 0}
if xx, ok := m["x"]; ok {
xx.count = 2
m["x"] = xx
} else {
panic("X isn't in the map")
}
}
This example can be run here: http://play.golang.org/p/OawL6QIXuO

As siritinga already pointed out, the elements of a map isn't ordered, so you cannot sort it.
What you can do is to create a slice and sort the elements using the sort package:
package main
import (
"fmt"
"sort"
)
type dataSlice []*data
type data struct {
count int64
size int64
}
// Len is part of sort.Interface.
func (d dataSlice) Len() int {
return len(d)
}
// Swap is part of sort.Interface.
func (d dataSlice) Swap(i, j int) {
d[i], d[j] = d[j], d[i]
}
// Less is part of sort.Interface. We use count as the value to sort by
func (d dataSlice) Less(i, j int) bool {
return d[i].count < d[j].count
}
func main() {
m := map[string]*data {
"x": {0, 0},
"y": {2, 9},
"z": {1, 7},
}
s := make(dataSlice, 0, len(m))
for _, d := range m {
s = append(s, d)
}
// We just add 3 to one of our structs
d := m["x"]
d.count += 3
sort.Sort(s)
for _, d := range s {
fmt.Printf("%+v\n", *d)
}
}
Output:
{count:1 size:7}
{count:2 size:9}
{count:3 size:0}
Playground
Edit
Updated the example to use pointers and to include a map so that you can both do lookups and have a slice to sort over.

Related

A function that search for an interface{} over a slice of interface{} in Go

I'm trying to implement a function that takes an element of any type and a slice of the same type and search the first inside the second, giving it's position as result or -1 otherwise.
I'm not a Go expert, so my first thought was to pass the element to search as interface{} and the slice as []interface{}, but it didn't really work.
Here's what I tried:
package main
import (
"fmt"
)
func IsElementInListWithPos(element interface{}, list []interface{}) int {
for i := range list {
if list[i] == element {
return i
}
}
return -1
}
func main() {
list1 := []int{1, 2, 3, 4, 5, 6}
list2 := []string{"a", "b", "c", "d"}
pos1 := IsElementInListWithPos(3, list1)
pos2 := IsElementInListWithPos("a", list2)
fmt.Println(pos1, pos2)
}
It gives me the following errors:
cannot use list (type []int) as type []interface {} in argument to IsElementInListWithPos
cannot use list2 (type []string) as type []interface {} in argument to IsElementInListWithPos
Any idea how I could solve this issue without actually using two different functions?
Thanks in advance.
The sort package demonstrates how interfaces can be used to implement algorithms in a type-independent way.
Linear search requires two essential operations that depend on the haystack element type, Len and Equal. So we can write the following Haystack interface and a Search function that using it:
type Haystack interface {
Len() int
Equal(int, interface{}) bool
}
func Search(haystack Haystack, needle interface{}) int {
for i := 0; i < haystack.Len(); i++ {
if haystack.Equal(i, needle) {
return i
}
}
return -1
}
This makes writing implementations for Haystack simple, but not type-safe:
type Strings []string
func (s Strings) Len() int { return len(s) }
func (s Strings) Equal(i int, x interface{}) bool { return s[i] == x.(string) }
type Ints []int
func (s Ints) Len() int { return len(s) }
func (s Ints) Equal(i int, x interface{}) bool { return s[i] == x.(int) }
func main() {
strings := []string{"b", "a", "c", "d"}
fmt.Println(Search(Strings(strings), "c")) // 2
fmt.Println(Search(Strings(strings), "e")) // -1
ints := []int{2, 1, 3, 4}
fmt.Println(Search(Ints(ints), 3)) // 2
fmt.Println(Search(Ints(ints), 5)) // -1
}
Note the type assertions in the Equal methods. To make this type-safe we have to get rid of the interface{} argument to Equal:
type Haystack interface {
Len() int
Equal(int) bool
}
func Search(haystack Haystack) int {
for i := 0; i < haystack.Len(); i++ {
if haystack.Equal(i) {
return i
}
}
return -1
}
type Strings struct {
hs []string
needle string
}
func (s Strings) Len() int { return len(s.hs) }
func (s Strings) Equal(i int) bool { return s.hs[i] == s.needle }
type Ints struct {
hs []int
needle int
}
func (s Ints) Len() int { return len(s.hs) }
func (s Ints) Equal(i int) bool { return s.hs[i] == s.needle }
func main() {
strings := []string{"b", "a", "c", "d"}
fmt.Println(Search(Strings{strings, "c"})) // 2
fmt.Println(Search(Strings{strings, "e"})) // -1
ints := []int{2, 1, 3, 4}
fmt.Println(Search(Ints{ints, 3})) // 2
fmt.Println(Search(Ints{ints, 5})) // -1
}
This made both the interface implementations and using the Search function much more complicated.
The moral of the story is that using interfaces this way requires a sufficiently complicated algorithm to be worth the trouble. If writing the interface implementation for a particular type is more work than writing the concrete implementation for the algorithm, well, then just write the concrete functions you need:
func SearchStr(haystack []string, needle string) int {
for i, x := range haystack {
if x == needle {
return i
}
}
return -1
}
func SearchInt(haystack []int, needle int) int {
for i, x := range haystack {
if x == needle {
return i
}
}
return -1
}
func main() {
strings := []string{"b", "a", "c", "d"}
fmt.Println(SearchStr(strings, "c")) // 2
fmt.Println(SearchStr(strings, "e")) // -1
ints := []int{2, 1, 3, 4}
fmt.Println(SearchInt(ints, 3)) // 2
fmt.Println(SearchInt(ints, 5)) // -1
}
Currently, it is not possible to build a solution that respects all your criteria. It will be possible once generics are implemented. Or you could try building one using reflect, but that will yield a complex and potentially slow solution... so I generally advise against using reflect for something as simple as this (see second snippet below).
What you can do right now is to use something like:
func FindFirst(n int, f func(int) bool) int {
for i := 0; i < n; i++ {
if f(i) {
return i
}
}
return -1
}
// in your code (s is the slice, e the value you are searching for)
i := FindFirst(len(s), func(i int) bool {
return s[i] == e
})
if i != -1 {
// i is the index of the element with value e
}
This, as you can imagine, does not make much sense... as it's arguably simpler, faster, and more idiomatic to simply write out the loop explicitly:
// in your code (s is the slice, e the value you are searching for)
for i, v := range s {
if v == e {
_ = i // i is the index of the element with value e
break
}
}
Obviously, this whole approach (linear scan) is only reasonable if the number of elements in the slice is small. If your slice is big and changes rarely, it would arguably make more sense (from a time complexity perspective) to sort it (sort.Slice) first and then do binary searches (sort.Search) on the sorted slice. Or, alternatively, you could use a map instead: in which case (assuming keys are small) lookup would be O(1).

Sort on map on value (attribute of Struct)

I have the below map:
detail := make(map[string]*Log)
type Log struct {
Id []string
Name []string
Priority int // value could be 1, 2, 3
Message string
}
I want to sort the "detail" map on basis of the value which is a struct in my case. This should be sorted by attribute "Priority".
For example, Log (map of struct) may have values similar to below:
Z : &{[ba60] [XYZ] 3 "I am the boss"}
B : &{[ca50] [ABC] 2 "I am the Junior"}
U : &{[zc20] [PQR] 1 "I am the Newbie"}
I want them to print from increasing Priority order i.e. 1 to 3
U : &{[zc20] [PQR] 1 "I am the Newbie"}
B : &{[ca50] [ABC] 2 "I am the Junior"}
Z : &{[ba60] [XYZ] 3 "I am the boss"}
I tried to use the sort and implemented the Sort interface, but seems like still missing the clue somewhere. So, I implemented the below interface:
type byPriority []*Log
func (d byPriority) Len() int {
return len(d)
}
func (d byPriority) Less(i, j int) bool {
return d[i].Priority < d[j].Priority
}
func (d byPriority) Swap(i, j int) {
d[i], d[j] = d[j], d[i]
}
But how should I apply sort.Sort() method on this map to get the sorted result. Do I need to add some more code?
The map type in Go is unordered. Regardless of what you do to a map, the next time you iterate over it you will receive the keys in random order. Thus there is no way to "sort" a map.
What you can do is copy the entries of the map into a slice, which is sortable.
package main
import (
"fmt"
"sort"
)
type Log struct {
Id []string
Name []string
Priority int // value could be 1, 2, 3
Message string
}
type Entry struct {
key string
value *Log
}
type byPriority []Entry
func (d byPriority) Len() int {
return len(d)
}
func (d byPriority) Less(i, j int) bool {
return d[i].value.Priority < d[j].value.Priority
}
func (d byPriority) Swap(i, j int) {
d[i], d[j] = d[j], d[i]
}
func printSorted(detail map[string]*Log) {
// Copy entries into a slice.
slice := make(byPriority, 0, len(detail))
for key, value := range detail {
slice = append(slice, Entry{key, value})
}
// Sort the slice.
sort.Sort(slice)
// Iterate and print the entries in sorted order.
for _, entry := range slice {
fmt.Printf("%s : %v\n", entry.key, entry.value)
}
}
func main() {
detail := map[string]*Log{
"Z": &Log{[]string{"ba60"}, []string{"XYZ"}, 3, "I am the boss"},
"B": &Log{[]string{"ca50"}, []string{"ABC"}, 2, "I am the Junior"},
"U": &Log{[]string{"zc20"}, []string{"PQR"}, 1, "I am the Newbie"},
}
printSorted(detail)
}

Traverse a Map in decreasing order of values

I'm trying to traverse a map in decreasing order of the values stored against keys. I've tried:
func frequencySort(s string) string {
var frequency map[string]int
chararray := strings.Split(s , "")
var a []int
var arranged map[int]string
for k , v := range frequency {
arranged[v] = k
}
for k := range arranged {
a = append(a , k)
}
sort.Sort(sort.Reverse(sort.IntSlice{a}))
}
Let's say the Map structure is :
"a" : 9
"b" : 7
"c" : 19
"d" : 11
and I'm trying to traverse it such that the output is :
"c" : 19
"d" : 11
"a" : 9
"b" : 7
The two map approach you have in your example will break as soon as you have more than one key in frequency with the same value, say "a":7 and "b":7, then you would lose data in arranged since keys have to be unique.
To avoid this you could create a helper type that will hold the map's contents temporarily, just for sorting purposes. Something like this:
package main
import (
"fmt"
"sort"
)
var m = map[string]int{
"a": 9,
"b": 7,
"c": 19,
"d": 11,
}
type entry struct {
val int
key string
}
type entries []entry
func (s entries) Len() int { return len(s) }
func (s entries) Less(i, j int) bool { return s[i].val < s[j].val }
func (s entries) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func main() {
var es entries
for k, v := range m {
es = append(es, entry{val: v, key: k})
}
sort.Sort(sort.Reverse(es))
for _, e := range es {
fmt.Printf("%q : %d\n", e.key, e.val)
}
}
https://play.golang.org/p/TPb0zNCtXO
For example,
package main
import (
"fmt"
"sort"
)
type frequncy struct {
c string
f int
}
func frequencies(s string) []frequncy {
m := make(map[string]int)
for _, r := range s {
m[string(r)]++
}
a := make([]frequncy, 0, len(m))
for c, f := range m {
a = append(a, frequncy{c: c, f: f})
}
sort.Slice(a, func(i, j int) bool { return a[i].f > a[j].f })
return a
}
func main() {
s := "aaaaabcbcbcbzxyyxzzsoaz"
fmt.Println(s)
f := frequencies(s)
fmt.Println(f)
}
Playground: https://play.golang.org/p/d9i3yL1x4K
Output:
aaaaabcbcbcbzxyyxzzsoaz
[{a 6} {b 4} {z 4} {c 3} {x 2} {y 2} {s 1} {o 1}]

How to recursively loop through map using different data structures

I'm trying to figure out the best way to recursively go through a [string]int map in Go. I'm building a game in which multiple countries are involved, and grouped together by teams of two in the end.
The goal is to match the first two countries with the lowest 'score' into its own group of two, and add it back to the collection giving the new map a total value of the scores of those countries.
Then recursively doing that to all the groups, ending up with one group and one total value in the end.
For example, if you had:
score := map[string]int{
"Canada": 7,
"US": 2,
"Germany": 3,
"Korea": 4,
}
group1 = {[US:2] [Germany:3]} with a total of 5
group1 would now be put back into the initial collection with a 'score' of 5 since it takes the two lowest scores. We would now have:
score := map[string]int{
"Canada": 7,
"Korea": 4,
group1: `US:2 Germany:3` with a total of 5
}
If this was now the lowest score in the collection, the next iteration would be:
group2 = {[Korea:4] [group1:5]}
score := map[string]int{
"Canada": 7,
group2: `Korea:4 group1:5` with a total of 9
}
And so on until you're left with one.. I think the basic structure should be something like this. However, I'm unsure of the proper way to do this since the data structure is now encompassing a [string]int map, as well as this new map.
I realize this is not such a generic question, but could an interface be used for this? I'm very new to Go, so advice would be helpful.
Here is an example to further illustrate what I mean:
https://play.golang.org/p/cnkTc0HBY4
Your problem can "easy" be solved using a heap data structure.
package main
import (
"container/heap"
"fmt"
)
// Something that has a score
type Scoreable interface {
fmt.Stringer
Score() int
}
// A country has a name and a score
type Country struct {
name string
score int
}
// Country implements Scoreable
func (c Country) Score() int {
return c.score
}
// ... and fmt.Stringer
func (c Country) String() string {
return fmt.Sprintf("%s [%d]", c.name, c.score)
}
// A team consists of two Scoreable's and has itself a score
type Team struct {
team1, team2 Scoreable
score int
}
// Team implements Scoreable
func (t Team) Score() int {
return t.score
}
// ... and fmt.Stringer
func (t Team) String() string {
return fmt.Sprintf("(%s + %s)", t.team1.String(), t.team2.String())
}
// The heap will be implemented using a slice of Scoreables
type TeamHeap []Scoreable
// TeamHeap implements heap.Interface
func (th TeamHeap) Len() int {
return len(th)
}
func (th TeamHeap) Less(i, j int) bool {
return th[i].Score() < th[j].Score()
}
func (th TeamHeap) Swap(i, j int) {
th[i], th[j] = th[j], th[i]
}
func (th *TeamHeap) Push(t interface{}) {
*th = append(*th, t.(Scoreable))
}
func (th *TeamHeap) Pop() interface{} {
old := *th
n := len(old)
t := old[n-1]
*th = old[0 : n-1]
return t
}
// The main function
func main() {
// Create a heap and initialize it
teams := &TeamHeap{}
heap.Init(teams)
// Push the countries (NB: heap.Push(), not teams.Push())
heap.Push(teams, Country{"Canada", 7})
heap.Push(teams, Country{"US", 2})
heap.Push(teams, Country{"Germany", 3})
heap.Push(teams, Country{"Korea", 4})
// Take the two teams with lowest score and make a new team of them
// Repeat this until there's only one team left
for teams.Len() > 1 {
t1 := heap.Pop(teams).(Scoreable)
t2 := heap.Pop(teams).(Scoreable)
heap.Push(teams, Team{t1, t2, t1.Score() + t2.Score()})
}
// Print the teams that we now have in the heap
for teams.Len() > 0 {
t := heap.Pop(teams).(Team)
fmt.Println(t)
}
}
You can find runnable code on the Go Playground.
package main
import (
"container/heap"
"fmt"
)
//Recursive data structure may looks something like
type Group struct {
Score int
Left *Group
Right *Group
Country string
}
//You can use slice to hold them organized in tree
type GrHeap []Group
//To implement your logic you can use stdlib/container/heap Heap interface
//you must implement Heap interface for your slice
func (h GrHeap) Len() int { return len(h) }
func (h GrHeap) Less(i, j int) bool { return h[i].Score < h[j].Score }
func (h GrHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *GrHeap) Push(x interface{}) {
// Push and Pop use pointer receivers because they modify the slice's length,
// not just its contents.
*h = append(*h, x.(Group))
}
func (h *GrHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
func main() {
//you most likely already have a map
//anyway it will be handy to keep it for convenient access to individual country
score := map[string]int{
"Canada": 7,
"US": 2,
"Germany": 3,
"Korea": 4,
}
//here we allocate heap
gr := make(GrHeap, 0)
//populate it from map
for k, v := range score {
g := Group{v, nil, nil, k}
gr = append(gr, g)
}
//and initialize
heap.Init(&gr)
//and here we use heap magic to implement your logic
for len(gr) > 2 {
l := heap.Pop(&gr).(Group)
r := heap.Pop(&gr).(Group)
ng := Group{l.Score + r.Score, &l, &r, ""}
heap.Push(&gr, ng)
}
fmt.Println(gr)
fmt.Println(gr[1].Left)
fmt.Println(gr[1].Right.Left)
//and you can see it works https://play.golang.org/p/gugJxJb7rr
}
You can try map[string]interface{} with Type assertion。
Here is the demo
package main
import "fmt"
const total = "total"
func GetValue(i interface{}) int {
value, ok := i.(int)
if ok {
return value
}
return i.(map[string]interface{})[total].(int)
}
func main() {
score := map[string]interface{}{
"Canada": 7,
"US": 2,
"Germany": 3,
"Korea": 4,
}
groupCount := 0
for len(score) > 2 {
var (
firstMin = math.MaxInt32
secondMin = math.MaxInt32
firstKey = ""
secondKey = ""
)
for k, v := range score {
iv := GetValue(v)
if iv < firstMin {
secondMin = firstMin
secondKey = firstKey
firstMin = iv
firstKey = k
continue
}
if iv < secondMin {
secondMin = iv
secondKey = k
continue
}
}
groupCount++
score[fmt.Sprintf("Group%d", groupCount)] = map[string]interface{}{
firstKey: score[firstKey],
secondKey: score[secondKey],
total: GetValue(score[firstKey])+ GetValue(score[secondKey]),
}
delete(score, firstKey)
delete(score, secondKey)
}
fmt.Println(score)
}
Here is the link https://play.golang.org/p/qq5qwAsh1m

Get the indices of the array after sorting in golang

I know we can use
sort.Sort(sort.Reverse(sort.IntSlice(example)))
to sort a array.
But how can I get the indices of the array?
e.g.
example := []int{1, 25, 3, 5, 4}
I want to get the output: 1, 3, 5, 4, 2
Make a wrapper for sort.IntSlice that remembers the indexes and swaps them when it swaps the values:
type Slice struct {
sort.IntSlice
idx []int
}
func (s Slice) Swap(i, j int) {
s.IntSlice.Swap(i, j)
s.idx[i], s.idx[j] = s.idx[j], s.idx[i]
}
Playground: http://play.golang.org/p/LnSLfe-fXk.
EDIT: As DaveC mentioned in the comments, you can actually wrap around sort.Interface to create a data structure for any sortable type:
type Slice struct {
sort.Interface
idx []int
}
func (s Slice) Swap(i, j int) {
s.Interface.Swap(i, j)
s.idx[i], s.idx[j] = s.idx[j], s.idx[i]
}
The accepted answer is good! But I needed a version of it that "didn't touch" my slice.
You can do it like this:
type sortable struct {
nums, idxs []int
}
func (s sortable) Len() int { return len(s.nums) }
func (s sortable) Less(i, j int) bool { return s.nums[i] < s.nums[j] }
func (s sortable) Swap(i, j int) {
s.nums[i], s.nums[j] = s.nums[j], s.nums[i]
s.idxs[i], s.idxs[j] = s.idxs[j], s.idxs[i]
}
func sortAndReturnIdxs(nums []int) []int {
idxs := make([]int, len(nums))
for i := range idxs {
idxs[i] = i
}
sort.Sort(sortable{nums, idxs})
return idxs
}
And then you can just call the function on your slice:
func main() {
nums := []int{4, 1, 6}
fmt.Println(sortAndReturnIdxs(nums)) // [1 0 2]
fmt.Println(nums) // [1 4 6]
}
The function returns the indices, and sorts the array in place.
Playground link: https://go.dev/play/p/7tli8tNeWNt
You can also just get the indices and avoid mutating the slice in-place, see https://github.com/mkmik/argsort

Resources