New package in Go standard library that makes easier to work with slices.
As part of the new Go release, several exciting changes have been introduced
to the Go ecosystem. While we’ve explored some of these changes in other articles about the
maps package and the cmp package,
there’s much more to discover beyond these two packages.
In this article, we’ll focus on the first part of the slices package, specifically
its new search functionality. Like many other updates and newly introduced packages, this one is also built upon the
foundation of generics, which were introduced in Go 1.18.
BinarySearch and BinarySearchFunc
Let’s start by exploring the first pair of functions designed for efficiently searching a target value within sorted slices.
In this context, we’re referring to the well-known Binary Search algorithm,
which is renowned as one of the most significant algorithms and is frequently used in coding interviews. Below, you’ll
find the signatures of both of these functions:
funcBinarySearchFunc[S~E, E, Tany](xS, targetT, cmpfunc(E, T) int) (int, bool)
Looking at the signatures of both functions, we can identify some small differences between them, and these differences
serve specific purposes. The first function, BinarySearch, expects two arguments. The first argument should be a slice
of sorted items, and it must adhere to the Ordered
constraint. When the items are ordered, the algorithm can efficiently compare them using the
Compare function from the cmp package.
On the other hand, the second function, BinarySearchFunc, is more versatile. It allows searching within slices where
the items don’t necessarily conform to the Ordered constraint. This flexibility is achieved by introducing a third
argument, the comparison function. This function is responsible for comparing items and determining their order. It
will be called by the BinarySearchFunc itself to make comparisons.
Both functions return two values. The first value is the index of the item within the slice, and the second is a boolean
value indicating whether the item was found in the slice or not. Let’s explore some examples below:
Take a close look at the results returned by the BinarySearch function, especially when the item doesn’t exist in the
slice. In our examples, we encountered four such cases where the function returned 0, 2, 5, and 6. When the
requested item isn’t present in the slice, the function indicates where it should be positioned if it were to be added
to the slice. Since the slice is sorted, it’s possible to determine the appropriate position for the item within the slice.
Here’s how it works:
If the target item is less than all other items in the slice, it should be placed at index 0.
If the target item is greater than all other items, it should be positioned at the end of the slice, which falls outside the index range of the current slice.
Otherwise, the function calculates the suitable position for the item within the slice.
Now, let’s explore the cases for the other function, BinarySearchFunc.
Here, we have simple examples using BinarySearchFunc, where we employed the same test cases as with the BinarySearch
function. However, this time, we had to provide a comparison function as an argument. In this case, we utilized the
Compare function from Go’s
But what’s the real purpose of BinarySearchFunc then? Let’s explore the example below to understand its significance.
In this example, we can see the true significance of BinarySearchFunc. If we intend to search for an element in a slice
that doesn’t consist of items of type Ordered, as in this case with a simple struct Exam, then we should use this
method. We can define our own comparison function to adapt the provided binary search solution to handle our specific
Min and MinFunc
Now, let’s discuss something that we can easily understand just from the function names. Yes, we are talking about the
functions Min and MinFunc. We have already discussed the built-in
min and max functions, and the functions
in the slices package heavily rely on them. But before we dive into further explanation, let’s take a look at their signatures:
funcMin[S~E, Ecmp.Ordered](xS) E
funcMinFunc[S~E, Eany](xS, cmpfunc(a, bE) int) E
The purpose of both new functions is to find the minimum value in the slice provided as an argument to the function. In
the case of the Min function, the slice must contain items of the Ordered type, while in the case of the MinFunc,
there is no such requirement, but we must provide a comparison function as a second argument.
In the provided examples, we can see that both functions work as expected. Min works fine with all types that are of
the Ordered type, and for other types, we should use MinFunc. One important note for these two methods is that both
expect to receive a slice with at least one element as an argument. In case that doesn’t happen, both functions will
panic, so make sure to handle this use case in your code.
Max and MaxFunc
I’ll keep this section as brief as possible since this new pair of functions is a continuation of the previous pair. So,
let’s take a look at them:
funcMax[S~E, Ecmp.Ordered](xS) E
funcMaxFunc[S~E, Eany](xS, cmpfunc(a, bE) int) E
As in all previous examples, here too, the Max and MaxFunc combo follows the same approach. Both functions search
for the maximum value in the provided slice, which must have at least one item; otherwise, both functions will panic.
Similar to the previous example, MaxFunc provides support for searching for the maximum in slices whose elements are
not of the type Ordered, but you should also provide the comparison function as an argument.
IsSorted and IsSortedFunc
Now, let’s discuss two slightly different functions: IsSorted and IsSortedFunc. Below, you can find their signatures:
These two new functions check if the slices provided as arguments are sorted in ascending order. The only difference between
them is that IsSorted, as in some previous examples, only works with slices of items that are of type Ordered, whereas
the function IsSortedFunc accepts items of any type, but it is necessary that we provide our own comparison function
as the second argument.
As we can observe, the IsSorted function returns a straightforward boolean value, indicating whether a slice is sorted
in ascending order. It’s essential to handle empty slices as an edge case, where the function returns true. Therefore,
this scenario should be appropriately addressed in the implementation. Similarly, we can utilize the IsSortedFunc
function in a similar fashion:
Just like in all the previous examples involving functions that require custom comparison logic, there are no surprises
here. The IsSortedFunc function allows us to check if a slice is sorted, without any requirement for the items to be
of type Ordered. It’s worth noting that we can effortlessly check if a slice is sorted in descending order by providing
our custom comparison function, which returns opposite values compared to the Compare function.
New version of Golang, 1.21, delivered many new updates, affecting standard library as well. In this article we checked
how some functions from slices packages work. Those new methods give us now possibility to easily check ordering of the
items of any slice that we want.