4

Slices Package: Clip, Clone, and Compact

 1 year ago
source link: https://www.ardanlabs.com/blog/2023/08/golang-slices-clip-clone-compact.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
August 7, 2023

Slices Package: Clip, Clone, and Compact

5 min read Go Programing

Introduction

In the first post of this series, I discussed the binary search API from the slices package that is now part of the standard library with the release of versionb 1.21. In this post, I will share how the Clip, Clone, and Compact APIs work.

The Clip API can be used to remove unused capacity from a slice. This means reducing the capacity of the slice to match its length.

The code that will be reviewed for this example can be found here.

https://github.com/ardanlabs/gotraining/blob/master/topics/go/packages/slices/example2/example2.go

To start, a slice with some capacity is needed.

Listing 1

01 package main
02
03 import (
04	"fmt"
05	"slices"
06 )
07
08 func main() {
09	list := make([]string, 0, 10)
10	fmt.Printf("Len(%d), Cap(%d)\n", len(list), cap(list))

In listing 1 on line 05, you can see the import for the new slices package from the standard library. Then on line 09, a slice with a length of 0 and a capacity of 10 is constructed. Finally on line 10, the length and capacity of the slice is printed.

Figure 1

184_figure1.png

Figure 1 shows the slice value and underlying array after the construction. You can see the length of the size is 0 and the capacity is 10.

Next, a value needs to be appended to the slice, which will change the slice’s length.

Listing 2

12	list = append(list, "A")
13	fmt.Printf("Len(%d), Cap(%d)\n", len(list), cap(list))

In listing 2 on line 12, the letter A is appended to the slice and then the slice’s length and capacity is printed again.

Figure 2

184_figure2.png

Figure 2 shows the slice value and underlying array after the append operation. You can see the length changed to 1 and the capacity remained the same at 10.

Now the Clip API can be used to reduce the capacity of the slice to match the length.

Listing 3

15	list = slices.Clip(list)
16	fmt.Printf("Len(%d), Cap(%d)\n", len(list), cap(list))

In listing 3 on line 15, the Clip API is called and the length and capacity of the slice is printed one last time.

Figure 3

184_figure3.png

Figure 3 shows the slice value and underlying array after the clip operation. As expected, the length of the slice remained at 1 and the capacity changed to 1 as well.

Clone

The Clone API performs a shallow copy of each element in the backing array. A shallow copy means each element is duplicated, but any values that the element might be pointing to are not duplicated. Those values are shared between the two duplicated elements.

The code that will be reviewed for this example can be found here.

https://github.com/ardanlabs/gotraining/blob/master/topics/go/packages/slices/example3/example3.go

To start, a slice with some length and capacity is needed.

Listing 5

01 package main
02
03 import (
04	"fmt"
05	"slices"
06 )
07
08 func main() {
09	list := []int{1, 2, 3, 4, 5}
10	fmt.Printf("List: Addr(%x), %v\n", &list[0], list)

In listing 5 on line 09, a slice is constructed with five values and then the address of the first element of the backing array and all five elements are printed.

Figure 4

184_figure4.png

Figure 4 shows the slice value and underlying array after the construction. You can see the slice has a length of 5 and a capacity of 5.

Listing 6

12	list = slices.Clone(list)
13	fmt.Printf("Copy: Addr(%x), %v\n", &list[0], list)

In listing 6 on line 12, the Clone API is called and then the address of the first element of the backing array and all five elements are printed again.

Figure 5

184_figure5.png

Figure 5 shows what happened after the Clone API was called. A second slice value is constructed with its own backing array that is a shallow copy of the original slice. Notice the address of the first element is different.

Compact

The Compact API removes consecutive duplicate elements from a slice.

The code that will be reviewed for this example can be found here.

https://github.com/ardanlabs/gotraining/blob/master/topics/go/packages/slices/example4/example4.go

There are two versions of the Compact API:

One that takes a slice and returns a “compacted” version of it. One that takes a slice and a custom compare function.

To start, a slice with some length and capacity is needed.

Listing 8

01 package main
02
03 import (
04	"fmt"
05	"slices"
06 )
07
08 func main() {
09    list := []int{1, 1, 2, 2, 1, 1, 3, 3, 4, 5}
10	fmt.Printf("List: Addr(%x), %v\n", &list[0], list)

In listing 8 on line 09, a slice with ten elements having some consecutive duplicates is constructed. Then the address of the first element of the backing array and all ten elements are printed.

Figure 6

184_figure6.png

Figure 6 shows the slice value and underlying array after the construction. You can see the length and capacity of the slice if 10.

Listing 9

12	list = slices.Compact(list)
13	fmt.Printf("List: Addr(%x), %v\n", &list[0], list)

In listing 9 on line 12, the Compact API is called and the address of the first element of the backing array and all ten elements are printed again.

Figure 7

184_figure7.png

Figure 7 shows what happened after the Compact API was called. The slice value is still pointing to the original backing array and the length of the slice has changed from 10 to 6. All the elements associated with the length have been compacted to the front of the backing array.

There may be times when you need to compact slices that are based on a struct type and need to customize how the algorithm identifies a duplicate.

Listing 11

22 // compare needs to return true if the two values are the same.
23 func compare(b int, a int) bool {
24	return a == b
25 }

Listing 11 represents a custom compare function that can be used with the CompactFunc API. Thanks to generics, a function that takes two values of some type T (in this case an integer) can be declared, implemented, and then passed to the API.

Listing 12

15	list = []int{1, 1, 2, 2, 1, 1, 3, 3, 4, 5}
16	fmt.Printf("List: Addr(%x), %v\n", &list[0], list)
17
18	list = slices.CompactFunc(list, compare)
19	fmt.Printf("List: Addr(%x), %v\n", &list[0], list)

In listing 12 on line 15, the slice is constructed and printed again. Then on line 18, the CompactFunc API is called using the custom compare function.

Figure 8

184_figure8.png

Figure 8 illustrates how the a and b parameters are determined and passed to the compare function on each iteration the CompactFunc API performs under the covers.

Figure 9

184_figure9.png

Figure 9 shows how the previous compare removed the element that was a duplicate of the value 1 and compacted the remaining elements back. Then the algorithm compares the next two elements.

Figure 10

184_figure10.png

Figure 10 shows once again how the duplicate of value 2 is removed and the remaining elements are compacted once more.

Conclusion

You can see how cool this new slices package is by providing the Clip, Clone, and Compact APIs. In the next post, we will explore the Compare and CompareFunc APIs.

If you want to cheat and see all the examples I have prepared for future posts, check out the Go training repo.

https://github.com/ardanlabs/gotraining/tree/master/topics/go/packages/slices


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK