Golang ProgramsGolang Programs

Slices

A slice is a flexible and extensible data structure to implement and manage collections of data. A slice is a segment of dynamic arrays that can grow and shrink as you see fit. Like arrays, slices are indexable and have a length. Unlike arrays they're flexible in terms of length because they have their own built-in function called append, which can grow a slice quickly with efficiency. You can also trim the size of a slice by slicing out a part of the hidden memory.

Slices helps us in indexing, iteration, and garbage collection optimizations because the hidden memory is allocated in adjoining blocks.

Creating Slices

There are three common ways to declare slices.

1) A basic declaration of an slice using make() function

make([]Type, length,capacity)

A slice can be initialized at runtime using the built-in function make

An example program that declares slices in golang:

package main

import "fmt"

func main() {

	var intSlice1 = make([]int,10)	// when length and capacity is same
	var intSlice2 = make([]int,10,20) // when length and capacity is different

	fmt.Printf("intSlice1 \tLen: %v \tCap: %v\n", len(intSlice1), cap(intSlice1))
	fmt.Printf("intSlice1 \tLen: %v \tCap: %v\n", len(intSlice2), cap(intSlice2))
}
golang slices

Creates an underlying array of type [10]int

Creates the slice value with length and capacity of 10

Third parameter that specifies the maximum capacity of the slice is optional.

2) Declaration using new keyword

package main

import "fmt"

func main() {
	var intSlice1 = new([50]int)[0:10]
	fmt.Printf("intSlice1 \tLen: %v \tCap: %v\n", len(intSlice1), cap(intSlice1))	
}
golang slices

In above declaration slice starts with a length of 10, going from 0 to 50.

Capacity is 50 so it can expand up-to 50 without requesting a new array.

3) Literal declaration

[]slice_type{comma-separated list of element values}

The following code snippet illustrates slice variables initialized with composite literal values:

package main

import "fmt"

func main() {
	var intSlice = []int{10,20,30,40}
	var strSlice = []string{"India","Canada","Japan"}
	fmt.Printf("intSlice \tLen: %v \tCap: %v\n", len(intSlice), cap(intSlice))
	fmt.Println(intSlice)	
	fmt.Printf("strSlice \tLen: %v \tCap: %v\n", len(strSlice), cap(strSlice))	
	fmt.Println(strSlice)
}
golang slices

The length and capacity are of 3 and 4 elements respectively.

Number of elements provided in the literal is not surrounded by a fixed size. This suggests that the literal can be as large as needed.

Slice Exercise

1) Enlarge a slice using the append function

append adds elements onto the end of a slice. If there's sufficient capacity in the underlying array, the element is placed after the last element and the length is incremented. However, if there is not sufficient capacity, a new array is created, all of the existing elements are copied over, the new element is added onto the end, and the new slice is returned.

The following code snippet enlarge a slice using the append Function:

package main

import "fmt"

func main() {
	// Create a smaller slice
	a := make([]int, 2, 5)
	a[0] = 10	
	a[1] = 20	
	fmt.Println("Slice A:", a)
	fmt.Printf("Length is %d Capacity is %d\n", len(a), cap(a))
	
	// Create a bigger slice
	a = append(a, 30, 40, 50, 60, 70, 80, 90)
	fmt.Println("Slice A after appending data:", a)
	fmt.Printf("Length is %d Capacity is %d\n", len(a), cap(a))	
}
golang slices

Using make(), create a slice of type []int and name it a

Declare a length of 2 and capacity of 5 elements.

Idiomatic way in Go is to use := opertor instead of var when creating slice inside main function.

7 elements added in slice using append function which increased the length to 9 and capacity to 12.

2) Enlarge a slice using the copy function

Go's built-in copy function is used to copy data from one slice to another. copy takes two arguments: dst and src. All of the entries in src are copied into dst overwriting whatever is there. If the lengths of the two slices are not the same, the smaller of the two will be used.

The following code snippet enlarge a slice using the copy Function:

package main

import "fmt"

func main() {
	// Create a smaller slice
	a := []int{5, 6, 7}
	fmt.Printf("[Slice:A] Length is %d Capacity is %d\n", len(a), cap(a))
	// Create a bigger slice
	b := make([]int, 5, 10)
	copy(b, a)	// copy function
	fmt.Printf("[Slice:B] Length is %d Capacity is %d\n", len(b), cap(b))

	fmt.Println("Slice B after copying:", b)
	b[3] = 8
	b[4] = 9
	fmt.Println("Slice B after adding elements:", b)
}
golang slices

Using make(), create a slice of type []int and name it b

Declare a length of 5 and capacity of 10 elements.

copy funcation copies all elements for a into b

3) Slice tricks

The following code snippet illustrates various slicing tricks:

package main

import "fmt"

func main() {	
	var countries = []string{"india", "japan", "canada", "australia", "russia"}
	
	fmt.Printf("Countries: %v\n", countries)
	
	fmt.Printf(":2 %v\n", countries[:2])

	fmt.Printf("1:3 %v\n", countries[1:3])
	
	fmt.Printf("2: %v\n", countries[2:])
	
	fmt.Printf("2:5 %v\n", countries[2:5])

	fmt.Printf("0:3 %v\n", countries[0:3])
	
	fmt.Printf("Last element: %v\n", countries[4])
	fmt.Printf("Last element: %v\n", countries[len(countries)-1])
	fmt.Printf("Last element: %v\n", countries[4:])
	
	fmt.Printf("All elements: %v\n", countries[0:len(countries)])

	fmt.Printf("Last two elements: %v\n", countries[3:len(countries)])
	fmt.Printf("Last two elements: %v\n", countries[len(countries)-2:len(countries)])
	
	fmt.Println(countries[:])
	fmt.Println(countries[0:])
	fmt.Println(countries[0:len(countries)])
}
golang slices

Declare a slice with 5 elements.

Using square bracket we can create different slices of a slices

len function used to calculate the length of slice.

4) Assign parts of slice to another slice

This technique illustrates the usage of ranges to select elements of a slice.

package main

import "fmt"

func main() {	
	var oldStr = []string{"india", "japan", "canada", "australia", "russia"}
	var strSlice []string
	newStr := oldStr[0:3]
	strSlice = append(strSlice, oldStr[:1]...)
	fmt.Printf("newStr: %v\n", newStr)
	fmt.Printf("strSlice: %v\n", strSlice)
	
	fmt.Printf("oldStr length: %v\tcapacity: %v\n", len(oldStr), cap(oldStr))
	fmt.Printf("newStr length: %v\tcapacity: %v\n", len(newStr), cap(newStr))
	fmt.Printf("strSlice length: %v\tcapacity: %v\n", len(strSlice), cap(strSlice))
	
	newStr[0] = "china"
	fmt.Printf("newStr: %v\n", newStr)
	fmt.Printf("oldStr: %v\n", oldStr)
	
	newStr = append(newStr, "brazil")
	fmt.Printf("newStr: %v\n", newStr)
	fmt.Printf("oldStr: %v\n", oldStr)
	
	oldStr = append(oldStr, "us")
	newStr = append(newStr, "uk")
	fmt.Printf("newStr: %v\n", newStr)
	fmt.Printf("oldStr: %v\n", oldStr)
}
golang slices

Declare a slice with 5 elements.

Declare slice newStr assign first three values of oldStr

5) Append a slice to an existing slice

This technique illustrates the usage of triple-dot ... ellipsis to append a slice.

package main

import "fmt"

func main() {	
	var slice1 = []string{"india", "japan", "canada"}
	var slice2 = []string{"australia", "russia"}
	slice2 = append(slice2, slice1...)
	fmt.Printf("slice1: %v\n", slice1)
	fmt.Printf("slice2: %v\n", slice2)
}
golang slices

Declare two slices with 2 and 3 items.

Append, the first slice slice1 to the second slice slice2

6) Append part of slice to an existing slice

This technique illustrates the usage of ellipsis with range to append a slice.

package main

import "fmt"

func main() {	
	var slice1 = []string{"india", "japan", "canada", "us", "uk", "italy", "germany"}
	var slice2 = []string{"australia", "russia"}
	slice2 = append(slice2, slice1[3:]...)	
	fmt.Printf("slice1: %v\n", slice1)
	fmt.Printf("slice2: %v\n", slice2)
}
golang slices

Declare two slices with 2 and 3 items.

Append, the first slice slice1 to the second slice slice2