Go - Sorting Arrays or Slices

发布时间 2023-10-08 10:51:22作者: ZhangZhihuiAAA

Problem: You want to sort elements in an array or slice.


Solution: For int , float64 , and string arrays or slices you can use sort.Ints , sort.Float64s , and sort.Strings . You can also use a custom comparator by using sort.Slice . For structs, you can create a sortable interface by implementing the sort.Interface interface and then using sort.Sort to sort the array or slice.

 

integers   :=   [] int { 3 ,   14 ,   159 ,   26 ,   53 } 
floats   :=   [] float64 { 3.14 ,   1.41 ,   1.73 ,   2.72 ,   4.53 } 
strings   :=   [] string { "the" ,   "quick" ,   "brown" ,   "fox" ,   "jumped" } 

sort . Ints ( integers ) 
sort . Float64s ( floats ) 
sort . Strings ( strings ) 

fmt . Println ( integers ) 
fmt . Println ( floats ) 
fmt . Println ( strings )

 

[3  14  26  53  159]
[1.41  1.73  2.72  3.14  4.53]
[brown  fox  jumped  quick  the]

This is sorted in ascending order. What if you want to sort it in descending order? There is no ready - made function to sort in descending order, but you can easily use a simple for loop to reverse the sorted slice:

for   i   :=   len ( integers ) / 2   -   1 ;   i   >=   0 ;   i - -   { 
      opp   :=   len ( integers )   -   1   -   i 
      integers [ i ],   integers [ opp ]   =   integers [ opp ],   integers [ i ] 
} 

fmt . Println ( integers )

Simply find the middle of the slice, and then using a loop, exchange the elements with their opposite side, starting from that middle. If you run the preceding snippet, this is what you will get:

[159  53  26  14  3]

You can also use the sort.Slice function, passing in your less function:

sort . Slice ( floats ,   func ( i ,   j   int )   bool   { 
      return   floats [ i ]   >   floats [ j ] 
}) 
fmt . Println ( floats )

This will produce the following output:

[4.53  3.14  2.72  1.73  1.41]

The less function, the second parameter in the sort.Slice function, takes in two parameters i and j , indices of the consecutive elements of the slice. It’s supposed to return true if the element at i is less than the element at j when sorting.

What if the elements are the same? Using sort.Slice means the original order of the elements might be reversed (or remain the same). If you want the order to be consistently the same as the original, you can use sort.SliceStable .

 

The sort.Slice function works with slices of any type, so this means you can also sort custom structs:

people   :=   [] Person { 
      { "Alice" ,   22 }, 
      { "Bob" ,   18 }, 
      { "Charlie" ,   23 }, 
      { "Dave" ,   27 }, 
      { "Eve" ,   31 }, 
} 
sort . Slice ( people ,   func ( i ,   j   int )   bool   { 
      return   people [ i ]. Age   <   people [ j ]. Age 
}) 
fmt . Println ( people )

If you run the code you will get the following output, with the people slice sorted according to the ages of the people:

[{Bob  18}  {Alice  22}  {Charlie  23}  {Dave  27}  {Eve  31}]

Another way of sorting structs is by implementing the sort.Interface . Here’s how you can do this for the Person struct:

type   Person   struct   { 
      Name   string 
      Age    int 
} 

type   ByAge   [] Person 

func   ( a   ByAge )   Len ()   int             {   return   len ( a )   } 
func   ( a   ByAge )   Less ( i ,   j   int )   bool   {   return   a [ i ]. Age   <   a [ j ]. Age   } 
func   ( a   ByAge )   Swap ( i ,   j   int )        {   a [ i ],   a [ j ]   =   a [ j ],   a [ i ]   }

You want to sort a slice of structs, so you need to associate the interface functions to the slice, not the struct. Create a type named ByAge that is a slice of Person structs. Next, you associate the Len , Less , and Swap functions to ByAge , making it a struct that implements sort.Interface . The Less method here is the same as the one used in the sort.Slice function earlier.

Using this is quite simple. You cast people to ByAge , and pass that into sort.Sort

people   :=   [] Person { 
      { "Alice" ,   22 }, 
      { "Bob" ,   18 }, 
      { "Charlie" ,   23 }, 
      { "Dave" ,   27 }, 
      { "Eve" ,   31 }, 
}

sort . Sort ( ByAge ( people )) 
fmt . Println ( people )

If you run this code, you will see the following results:

[{Bob  18}  {Alice  22}  {Charlie  23}  {Dave  27}  {Eve  31}]

Implementing sort.Interface is a bit long - winded, but there are certainly some advantages. For one, you can use sort.Reverse to sort by descending order:

sort . Sort ( sort . Reverse ( ByAge ( people ))) 
fmt . Println ( people )

This produces the following output:

[{Eve  31}  {Dave  27}  {Charlie  23}  {Alice  22}  {Bob  18}]

You can also use the sort.IsSorted function to check if the slice is already sorted:

sort . IsSorted ( ByAge ( people ))   //  true  if  it's  sorted

The biggest advantage, though, is that using sort.Interface is a lot more performant than using sort.Slice.

As you can see, using sort.Interface is more efficient. This is because sort.Slice uses any as the first parameter. This means it takes in any structs but is less efficient.