Exploring the Power of Go 1.21: slices Package
Introduction
Welcome to a tour of Go 1.21’s ‘slices’ upgrades! In this blog post, we’ll explore the enhancements this new package brings, ensuring better performance for your Go applications.
We are going to talk about the ‘slices’ package. With this package, we can perform different operations over slices in Go.
The first step is to import the slices package. (We’ll import the fmt package for displaying the examples.)
import (
"fmt"
"slices"
)
We are going to see those methods next:
- Binary Search: searches some value
- Compact: replaces consecutive runs of equal elements
- Compare: compares 2 slices
- Contains: checks if some value exists
- Clone: clones a slice
- Equals: determines whether two slices are equal.
- Index: returns the index of the value searched
- Insert: inserts a range of values
- IsSorted: checks whether our slices is sorted
- Max & Min: get the maximum or minimum value
- Remove: removes a range of values
- Reverse: reverses the elements
- Replace: replaces a range of elements
- Sort: sorts the elements
Some methods have a ‘Function’ functionality , with this we can use custom comparison.
Binary Search
We can perform a binary search with this method to find some value in our slice.
Our slices must be ordered to do this operation.
position, isFound := slices.BinarySearch(mySlice, value)
For example:
binarySrcTest := []int{3, 2, 4, 7,3, 1, 2,4,6}
i, found := slices.BinarySearch(binarySrcTest, 7)
fmt.Printf("BinarySearch - found: %t | position: %d\n", found, i)
// the slice must be ordered
slices.Sort(binarySrcTest)
i, found = slices.BinarySearch(binarySrcTest, 7)
fmt.Printf("BinarySearch - found: %t | position: %d\n", found, i)
Output:
BinarySearch - found: false | position: 9
BinarySearch - found: true | position: 8
Compact
This method replaces consecutive runs of equal elements with a single copy.
Our slices must be ordered to do this operation.
result := slices.Compact(mySlice)
For example:
compactTest := []int{1,1,2,2,9,9,3,3,2,1,10,10,5,1}
fmt.Printf("Compact: %v\n", slices.Compact(compactTest))
// the slice must be ordered
slices.Sort(compactTest)
fmt.Printf("Compact: %v\n", slices.Compact(compactTest))
Output:
Compact: [1 2 9 3 2 1 10 5 1]
Compact: [1 2 3 5 9 10]
Compare
We can compare 2 slices. The result is:
- 0 if s1 == s2
- -1 if s1 < s2
- +1 if s1 > s2
result := slices.Compare(mySlice1, mySlice2)
For example:
compare1Test := []int{3, 2, 4, 3, 1, 2,4,6}
compare2Test := []int{3, 2, 4, 3, 1, 2,4,6}
r := slices.Compare(compare1Test, compare2Test)
fmt.Printf("Compare: %d - %v & %v\n", r, compare1Test, compare2Test)
compare2Test[2] = 5
r = slices.Compare(compare1Test, compare2Test)
fmt.Printf("Compare: %d - %v & %v\n", r, compare1Test, compare2Test)
Output:
Compare: 0 - [3 2 4 3 1 2 4 6] & [3 2 4 3 1 2 4 6]
Compare: -1 - [3 2 4 3 1 2 4 6] & [3 2 5 3 1 2 4 6]
Contains
We can use this method to check if some value exists in the slice.
exist := slices.Contains(mySlice, someValue)
For example:
containsTest := []int{1,2,3,3,1,2,8,1}
fmt.Printf("Contains: %v\n", slices.Contains(containsTest, 3))
fmt.Printf("Contains: %v\n", slices.Contains(containsTest, 9))
Output:
Contains: true
Contains: false
Clone
We can use this method to clone a slice
clonedSlice := slices.Clone(mySlice)
For example:
cloneTest := []int{3, 2, 4, 3, 1, 2,4,6}
fmt.Printf("cloneTest value: %v\n", cloneTest)
clonedTest := slices.Clone(cloneTest)
cloneTest[2] = 5 // change same value
fmt.Printf("Clone: %v & %v\n", cloneTest, clonedTest)
Output:
cloneTest value: [3 2 4 3 1 2 4 6]
Clone: [3 2 5 3 1 2 4 6] & [3 2 4 3 1 2 4 6]
Equals
We can use this method to determine whether two slices are equal: they must have the same length, and all elements must be equal.
If the lengths are different, the method will return false.
Otherwise, the elements are compared in increasing index order, and the comparison stops at the first unequal pair.
isEqual := slices.Equal(mySlice1, mySlice2)
For example:
equalTest := []int{40,1,5,1,3}
fmt.Printf("Equal: %t\n", slices.Equal(equalTest, []int{40,5,1,1,3}))
fmt.Printf("Equal: %t\n", slices.Equal(equalTest, []int{4,3}))
fmt.Printf("Equal: %t\n", slices.Equal(equalTest, []int{40,1,5,1,3}))
Output:
Equal: false
Equal: false
Equal: true
Index
This method returns the index of the first occurrence of the value in our slice, or -1 if not present.
position := slices.Index(mySlice, value)
For example:
indexTest := []int{1,2,3,1,2,8}
fmt.Printf("Index: %d\n", slices.Index(indexTest, 8))
fmt.Printf("Index: %d\n", slices.Index(indexTest, 2))
fmt.Printf("Index: %d\n", slices.Index(indexTest, 9))
Output:
Index: 5
Index: 1
Index: -1
Insert
We can use this method to insert a range of values at a specific index and return the modified slice.
newSlice := slices.Insert(mySlice, index, value1, value2, ...)
For example:
insertTest := []int{1,2,3,3,1,2,8,1}
fmt.Printf("Insert: %v\n", slices.Insert(insertTest, 4, 10, 20, 22))
fmt.Printf("Insert: %v\n", slices.Insert(insertTest, 6, 11, 11, 11))
Output:
Insert: [1 2 3 3 10 20 22 1 2 8 1]
Insert: [1 2 3 3 1 2 11 11 11 8 1]
IsSorted
We can use this method to check whether our slice is sorted in ascending order.
result := slices.IsSorted(mySlice)
For example:
isSortedTest := []int{1,2,3,1,2,8}
fmt.Printf("IsSorted: %t\n", slices.IsSorted(isSortedTest))
slices.Sort(isSortedTest)
fmt.Printf("IsSorted: %t\n", slices.IsSorted(isSortedTest))
Output:
IsSorted: false
IsSorted: true
Max & Min
We can use the Max and Min methods to get the maximum and minimum values of the slice.
maxValue := slices.Max(mySlice)
minValue := slices.Min(mySlice)
For example:
maxTest := []int{3, 2, 4, 3, 1, 2,4,6}
fmt.Printf("Max: %d\n", slices.Max(maxTest))
minTest := []int{3, 2, 4, 3, 1, 2,4,6}
fmt.Printf("Min: %d\n", slices.Min(minTest))
Output:
Max: 6
Min: 1
Remove
We can use this method to remove a range of our slice, and the method will return the new slice.
newSlice := slices.Delete(mySlice, from,to)
For example:
deleteTest := []int{3, 2, 4, 3, 1, 2,4,6}
fmt.Printf("deleteTest variable: %v\n", deleteTest)
fmt.Printf("Delete: %v\n", slices.Delete(deleteTest, 1,4))
Output:
deleteTest variable: [3 2 4 3 1 2 4 6]
Delete: [3 1 2 4 6]
Reverse
We can use this method to reverse the elements of our slice.
slices.Reverse(mySlice)
For example:
reverseTest := []int{3, 2, 4, 3, 1, 2,4,6}
fmt.Printf("reverseTest variable: %v\n", reverseTest)
slices.Reverse(reverseTest)
fmt.Printf("Reverse: %v\n", reverseTest)
Output:
reverseTest variable: [3 2 4 3 1 2 4 6]
Reverse: [6 4 2 1 3 4 2 3]
Replace
We can use this method to replace a range of elements. To perform this operation, we need to define the value of ‘i’ (start), the value of ‘j’ (end), and the values that will be added.
newSlice := slices.Replace(mySlice, from, to, value1, value2, ...)
It panics if mySlice[ from : to ] is not a valid slice.
For example, We will use the slice
index [0, 1, 2, 3, 4, 5, 6, 7]
values [3, 2, 4, 3, 1, 2, 4, 6]
We will replace the values from index 3 to index 6 with other values. It is not necessary for these other values to have the same length as the range (index 3 to index 6).
index [... , 3, 4, 5, ...]
values [... , 3, 1, 2, ...]
replaceTest := []int{3, 2, 4, 3, 1, 2,4,6}
fmt.Printf("replaceTest variable: %v\n", replaceTest)
replaceTest = slices.Replace(replaceTest, 3, 6, 10, 11, 12)
fmt.Printf("Replace: %v\n", replaceTest)
Output:
replaceTest variable: [3 2 4 3 1 2 4 6]
Replace: [3 2 4 10 11 12 4 6]
We can add the same 3 values only replacing 1 value
replaceTest := []int{3, 2, 4, 3, 1, 2,4,6}
fmt.Printf("replaceTest variable: %v\n", replaceTest)
replaceTest = slices.Replace(replaceTest, 3, 4, 10, 11, 12)
fmt.Printf("Replace: %v\n", replaceTest)
Output:
replaceTest variable: [3 2 4 3 1 2 4 6]
Replace: [3 2 4 10 11 12 1 2 4 6]
Or no values
replaceTest := []int{3, 2, 4, 3, 1, 2,4,6}
fmt.Printf("replaceTest variable: %v\n", replaceTest)
replaceTest = slices.Replace(replaceTest, 3, 3, 10, 11, 12)
fmt.Printf("Replace: %v\n", replaceTest)
Output:
replaceTest variable: [3 2 4 3 1 2 4 6]
Replace: [3 2 4 10 11 12 3 1 2 4 6]
Sort
We can use this method to sort the elements of our slice.
slices.Sort(mySlice)
For example:
sortTest := []int{3, 2, 4, 3, 1, 2,4,6}
fmt.Printf("sortTest variable: %v\n", sortTest)
slices.Sort(sortTest)
fmt.Printf("Sort: %v\n", sortTest)
Output:
sortTest variable: [3 2 4 3 1 2 4 6]
Sort: [1 2 2 3 3 4 4 6]
Function
Some methods have a ‘Function’ functionality, with which we can use custom comparison. Those methods are:
We are going to do the following example with the Equal method (Function method), comparing an integer slice with a string slice.
We defined the values for comparing and the function.
numbers := []int{0, 42, 8}
strings := []string{"000", "42", "0o10"}
equal := slices.EqualFunc(numbers, strings, func(n int, s string) bool {
sn, err := strconv.ParseInt(s, 0, 64)
if err != nil {
return false
}
return n == int(sn)
})
fmt.Printf("EqualFunc: %t\n", equal)
Output:
EqualFunc: true
Conclusion
In this article, we have presented an array of valuable methods to proficiently manipulate our slices. Remember, for more information you can see the Go official site here
resource: blog_go_v21_slices