Golang : Natural string sorting example
Problem:
You want to sort a couple of strings with numbers in natural order so that the list looks nice to your eyes or simply to prevent your program users from complaining that the items or filenames are not being sorted properly. For instance:
'Team 101', 'Team 58', 'Team 30', 'Team 1'
to
'Team 1', 'Team 30', 'Team 58', 'Team 101'
How to do that?
Solution:
Below is my adaptation of [https://golang.org/pkg/sort/#example__sortKeys][1]
. It should sort most strings with numbers. However, it won't be able to sort strings with decimal values such as version number properly. Will update in future once there is a better solution.
Here you go!
package main
import (
"fmt"
"log"
"sort"
"strconv"
"unicode"
"strings"
)
type Compare func(str1, str2 string) bool
func (cmp Compare) Sort(strs []string) {
strSort := &strSorter{
strs : strs,
cmp : cmp,
}
sort.Sort(strSort)
}
type strSorter struct {
strs []string
cmp func(str1, str2 string) bool
}
func extractNumberFromString(str string, size int) (num int) {
strSlice := make([]string, 0)
for _, v := range str {
if unicode.IsDigit(v) {
strSlice = append(strSlice, string(v))
}
}
if size == 0 { // default
num, err := strconv.Atoi(strings.Join(strSlice, ""))
if err != nil {
log.Fatal(err)
}
return num
} else {
num, err := strconv.Atoi(strSlice[size-1])
if err != nil {
log.Fatal(err)
}
return num
}
}
func (s *strSorter) Len() int { return len(s.strs) }
func (s *strSorter) Swap(i, j int) { s.strs[i], s.strs[j] = s.strs[j], s.strs[i] }
func (s *strSorter) Less(i, j int) bool { return s.cmp(s.strs[i], s.strs[j]) }
func main() {
// closure order for natural string number sorting
compareStringNumber := func(str1, str2 string) bool {
return extractNumberFromString(str1,0) < extractNumberFromString(str2,0)
}
fmt.Println("Natural or 'Human' string sort results:")
naturalStringSlice := []string{"Team11", "Team3", "Team9", "Team1"}
fmt.Println("Original : " , naturalStringSlice)
Compare(compareStringNumber).Sort(naturalStringSlice)
fmt.Println("Naturally sorted : ", naturalStringSlice)
naturalStringSlice2 := []string{"9th", "3rd", "10th", "1st"}
fmt.Println("Original : " , naturalStringSlice2)
Compare(compareStringNumber).Sort(naturalStringSlice2)
fmt.Println("Naturally sorted : ", naturalStringSlice2)
naturalStringSlice3 := []string{"A3","a5", "a30", "a1", "A9", "A7"}
fmt.Println("Original : " ,naturalStringSlice3)
Compare(compareStringNumber).Sort(naturalStringSlice3)
fmt.Println("Naturally sorted : ", naturalStringSlice3)
naturalStringSlice4 := []string{"Team 101","Team 58", "a30", "a1", "A9", "A7"}
fmt.Println("Original : " ,naturalStringSlice4)
Compare(compareStringNumber).Sort(naturalStringSlice4)
fmt.Println("Naturally sorted : ", naturalStringSlice4)
// closure order for chemical elements sorting
compareChemicalElements := func(str1, str2 string) bool {
return extractNumberFromString(str1,1) < extractNumberFromString(str2,1)
}
chemicalElements := []string{"C2H6", "C1H2", "C2N", "C1H4", "C1H2", "C1H4", "C2H2", "C3H6"}
//chemicalElements := []string{"C2N", "C1H3"} -- fixed!
fmt.Println("Original : " ,chemicalElements)
Compare(compareChemicalElements).Sort(chemicalElements)
fmt.Println("Naturally sorted : ", chemicalElements)
// won't work well with decimal values...need further enhancement/debugging
// closure order for chemical elements sorting
/*compareVersion := func(str1, str2 string) bool {
// remember ver-1.2.15 has size 3 digits
// why? because counting of index starts from 0 !
return extractNumberFromString(str1,3) < extractNumberFromString(str2,3)
}
//naturalStringSlice3 := []string{"ver-1.2.15", "ver-1.3.1", "ver-1.2.3", "ver-1.3.12", "ver-1.3.3", "ver-1.2.5"}
naturalStringSlice3 := []string{"ver-1.3.3","ver-1.2.5", "ver-1.3.1","ver-1.1.3", "ver-1.3.1", }
fmt.Println("Original : " ,naturalStringSlice3)
Compare(compareVersion).Sort(naturalStringSlice3)
fmt.Println("Naturally sorted : ",naturalStringSlice3)*/
}
Output:
Natural or 'Human' string sort results:
Original : [Team11 Team3 Team9 Team1]
Naturally sorted : [Team1 Team3 Team9 Team11]
Original : [9th 3rd 10th 1st]
Naturally sorted : [1st 3rd 9th 10th]
Original : [A3 a5 a30 a1 A9 A7]
Naturally sorted : [a1 A3 a5 A7 A9 a30]
Original : [Team 101 Team 58 a30 a1 A9 A7]
Naturally sorted : [a1 A7 A9 a30 Team 58 Team 101]
Original : [C2H6 C1H2 C2N C1H4 C1H2 C1H4 C2H2 C3H6]
Naturally sorted : [C1H2 C1H4 C1H2 C1H4 C2H6 C2N C2H2 C3H6]
Happy coding!
References:
https://blog.codinghorror.com/sorting-for-humans-natural-sort-order/
By Adam Ng
IF you gain some knowledge or the information here solved your programming problem. Please consider donating to the less fortunate or some charities that you like. Apart from donation, planting trees, volunteering or reducing your carbon footprint will be great too.
Advertisement
Tutorials
+8.8k Golang : Generate random Chinese, Japanese, Korean and other runes
+5.1k Unix/Linux/MacOSx : How to remove an environment variable ?
+12.1k Golang : HTTP response JSON encoded data
+13.6k Golang: Pad right or print ending(suffix) zero or spaces in fmt.Printf example
+5.2k Golang : Shortening import identifier
+7.7k Android Studio : Rating bar example
+8k Golang : Implementing class(object-oriented programming style)
+12.6k Golang : How to calculate the distance between two coordinates using Haversine formula
+14k Golang : How to determine if user agent is a mobile device example
+9.3k Javascript : Read/parse JSON data from HTTP response
+6.6k Golang : Get Alexa ranking data example
+6.4k How to let Facebook Login button redirect to a particular URL ?