Golang : Natural string sorting example



Tags : golang natural-sort alpha-numeric-sort chemical-elements

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/ https://golang.org/pkg/sort/#example__sortKeys http://www.davekoelle.com/alphanum.html


Tags : golang natural-sort alpha-numeric-sort chemical-elements

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

Advertisement

Advertisement