Exploring the Power of Go 1.21: slices Package.

Nahuel Costamagna

Nahuel Costamagna

· 10 min read
Go 1.21: slices Package - Nahuel Costamagna

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


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:

MethodFunction Method
BinarySearchBinarySearchFunc
CompactCompactFunc
CompareCompareFunc
ContainsContainsFunc
DeleteDeleteFunc
EqualEqualFunc
IndexIndexFunc
IsSortedIsSortedFunc
MaxMaxFunc
MinMinFunc
SortSortFunc

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

Nahuel Costamagna

Nahuel Costamagna

FullStack Developer & DevOps