I have a slice of 10 maps, how can I sort by one of the values?
[{"name":"a","score":100},
{"name":"z","score":150},
{"name":"s","score":120},
{"name":"x","score":110},
{"name":"r","score":10},
{"name":"b","score":1300},
{"name":"b","score":1500},
{"name":"w","score":1002},
{"name":"o","score":140},
{"name":"q","score":101}]
I want to sort the slice with the score value, in descending order, but I'm at a loss of how.
just a simple sort.Slice.
sort.Slice(information,
func(i, j int) bool {
return information[i].Difference > information[j].Difference
})
Related
In Go is possible to compare two strings:
package main
func main() {
println("ab" > "ba")
println("ab" < "ba")
}
false
true
Program exited.
https://go.dev/play/p/svkLf6R84SC
How do I perform a similar operation on two slices? e.g. []int{1,2} > []int{2,1}.
I need this to sort a slice of slices of ints. So I need an implementation of sort.Interface.
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
It would be better if this implementation is generic.
Writing a comparator and a less function for sort.Slices would be the most effective way to do this within the standard library.
Stepping outside of that slightly (until generics usage within the standard library is finalized), in Go 1.18 we can use the golang.org/x/exp/constraints and golang.org/x/exp/slices packages to generically sort a slice of slices of ordered values: https://go.dev/play/p/MA0lY6POVFR
func SortSlices[T constraints.Ordered](s [][]T) {
sort.Slice(s, func(i, j int) bool {
return slices.Compare(s[i], s[j]) < 0
})
}
Documentation about slices.Compare:
Compare compares the elements of s1 and s2. The elements are compared sequentially, starting at index 0, until one element is not equal to the other. The result of comparing the first non-matching elements is returned. If both slices are equal until one of them ends, the shorter slice is considered less than the longer one. The result is 0 if s1 == s2, -1 if s1 < s2, and +1 if s1 > s2. Comparisons involving floating point NaNs are ignored.
Please see this playground. I have a slice with some data. Now I want to sort this slice, but not on a value inside one of the values, but I want to sort it on the number of occurrences of one of the values.
Unfortunately I don't have a clue how to fix this. Can someone please point me to the right direction?
I know I can do this:
sort.Slice(messages, func(i, j int) bool {
return messages[i].SupplierID < messages[j].SupplierID
})
This will sort the slice, but it will start with the "higest" supplierID. but what I want is to sort on the number of occurrences: the ones with the lease number of supplierIDs first.
Iterate over the slice, populate a map of occurrences, then sort the slice based on the number of occurrences:
supplierCounts := make(map[int]int)
for _, msg := range messages {
supplierCounts[msg.SupplierID]++
}
sort.Slice(messages, func(i, j int) bool {
return supplierCounts[messages[i].SupplierID] < supplierCounts[messages[j].SupplierID]
})
https://play.golang.org/p/YMWPP2JBC2P
say I have an int slice containing the following numbers: 2781,4706,1347,1192,3087,2920,198,2312,930,3284,1677,3687,2011,4107,4148,4195,2159,1263,2918,2503
I want to sort them based on how close they are away from a certain value.
For example, say my value was 1200.
My slice after being sorted would be: 1192,1263,1347,1677,2011,2159,2312,2503,2781,2918,2920,3087,3284,3687,4107,4148,4195,4706,930,198
I tried the approach of iterating through the array, subtracting my specified value, then sorting in ascending order ultimately adding the value again to return to my original number. This approach wouldn't work in all test cases, and I know its bad practice since I'm mutating the array.
One approach would be to use sort.Slice(...) with a "less" (comparator) function that simply returns the lesser of the distance of the two given values from the target value.
For example (Go Playground):
func main() {
xs := []int{2781, 4706, 1347, 1192, 3087, 2920, 198, 2312, 930, 3284, 1677, 3687, 2011, 4107, 4148, 4195, 2159, 1263, 2918, 2503}
sortByDistanceFrom(1200, xs)
fmt.Println(xs)
// [1192 1263 1347 930 1677 2011 2159 198 2312 2503 2781 2918 2920 3087 3284 3687 4107 4148 4195 4706]
}
func sortByDistanceFrom(x int, xs []int) {
sort.Slice(xs, func(i, j int) bool {
di := math.Abs(float64(x - xs[i]))
dj := math.Abs(float64(x - xs[j]))
return di < dj
})
}
Note that the fourth value returned by this example is 930 instead of 1677 produced by your sample output because it computes the absolute distance to the target number (|1200-1677|=433 > |1200-930|=270). If you want to prefer values greater than the target to values less than the target then you would have to modify your comparator function accordingly.
Ok, i think this may be an old question, but i didn't find anything over the stackoverflow. In go , the iteration order over a map is not guranteed to be reproducible. So, the way suggest is to hold the keys in a slice and sort that slice. Then iterate over that slice to retrieve the values from the map, so that we get them in order(since slice composed of keys is sorted, so will be in reproducible order). So this goes to imply that the slice need be sorted else iteration over the slice will also not give reproducible order. But when i tried the below code in playground, i always found the order maintained in iteration, then in the map iteration case, why the slice of keys need to be sorted?
func main() {
var mySlice = make([]string, 0)
mySlice = append(mySlice, "abcd")
mySlice = append(mySlice, "efgh")
mySlice = append(mySlice, "ijkl")
mySlice = append(mySlice, "mnop")
mySlice = append(mySlice, "qrst")
mySlice = append(mySlice, "uvwxyz")
for _, val := range mySlice {
fmt.Println(val)
}
fmt.Println(strings.Join(mySlice, "|"))
}
Output:
abcd
efgh
ijkl
mnop
qrst
uvwxyz
abcd|efgh|ijkl|mnop|qrst|uvwxyz
A slice or array will always have a fixed order, i.e. how it is laid out in memory.
The documentation you were reading was probably just telling you to sort the slice so that the map output is in sorted order.
You are correct that the iteration order of a map is undefined and hence can be different each time it is performed. If you use a slice to iterate a map then it will always come back in a reliable order, i.e. the order of the keys in the slice.
I suggest you have a read over the information about slices.
EDIT
If it helps, consider the following code to illustrate that the sorting of a slice has nothing to do with its order being fixed:
words := map[int]string{
0: "hello",
1: "there",
2: "goodbye",
}
keys:=[]int{2,0,1}
for _, k := range keys {
// Will output in order: Goodbye, hello, there
fmt.Println("Key:", k, "Value:", words[k])
}
The only reason your slice is sorted is because you're appending items in already sorted order. If you appended items in an unsorted order like this
var mySlice = make([]string, 0)
mySlice = append(mySlice, "mnop")
mySlice = append(mySlice, "efgh")
mySlice = append(mySlice, "uvwxyz")
mySlice = append(mySlice, "ijkl")
mySlice = append(mySlice, "abcd")
mySlice = append(mySlice, "qrst")
(or populated a slice by pulling keys from a map, which would be unsorted), then the order on iteration would be unsorted (consistent, yes, but consistently unsorted). So, if your objective is to use the slice to pull items from a map in sorted order, then you need to first sort the slice, unless you can guarantee the slice items were inserted in an already sorted order.
What is the best way to check whether a certain value is in a string slice? I would use a Set in other languages, but Go doesn't have one.
My best try is this so far:
package main
import "fmt"
func main() {
list := []string{"a", "b", "x"}
fmt.Println(isValueInList("b", list))
fmt.Println(isValueInList("z", list))
}
func isValueInList(value string, list []string) bool {
for _, v := range list {
if v == value {
return true
}
}
return false
}
http://play.golang.org/p/gkwMz5j09n
This solution should be ok for small slices, but what to do for slices with many elements?
If you have a slice of strings in an arbitrary order, finding if a value exists in the slice requires O(n) time. This applies to all languages.
If you intend to do a search over and over again, you can use other data structures to make lookups faster. However, building these structures require at least O(n) time. So you will only get benefits if you do lookups using the data structure more than once.
For example, you could load your strings into a map. Then lookups would take O(1) time. Insertions also take O(1) time making the initial build take O(n) time:
set := make(map[string]bool)
for _, v := range list {
set[v] = true
}
fmt.Println(set["b"])
You can also sort your string slice and then do a binary search. Binary searches occur in O(log(n)) time. Building can take O(n*log(n)) time.
sort.Strings(list)
i := sort.SearchStrings(list, "b")
fmt.Println(i < len(list) && list[i] == "b")
Although in theory given an infinite number of values, a map is faster, in practice it is very likely searching a sorted list will be faster. You need to benchmark it yourself.
To replace sets you should use a map[string]struct{}. This is efficient and considered idiomatic, the "values" take absolutely no space.
Initialize the set:
set := make(map[string]struct{})
Put an item :
set["item"]=struct{}{}
Check whether an item is present:
_, isPresent := set["item"]
Remove an item:
delete(set, "item")
You can use a map, and have the value e.g. a bool
m := map[string] bool {"a":true, "b":true, "x":true}
if m["a"] { // will be false if "a" is not in the map
//it was in the map
}
There's also the sort package, so you could sort and binary search your slices