In the previous parts of this series, you have learned about variables and how they help you store a specific value for easy use later in your code. You also learned about the different data types in Go and how to use them to better represent information in your code. However, there are cases when these won't be sufficient regarding related data and other scenarios.
In this article, we will explore what data structures are in Go and how to use arrays, slices, and maps to work with related data in your Go applications.
Collection Types
Collection types in Go are used to store related data in a single unit for easy access and utilization. They enable you to group and access related data in your applications efficiently and fast.
Collection types in Go are a different approach to data structures, providing three different collection types arrays, slices, and maps. We will explore what these are in the following sections.
Arrays
Go allows you to store values of the same type in a collection called arrays. You can create an array in Go with a square bracket containing the length of the array followed by the data type of the values and, finally, the values of the array inside a pair of curly braces:
var classesOfFood = [6]string{"Carbohydrates", "Proteins", "Fats and Oils", "Vitamins", "Minerals", "Water"}
The code above defines an array, classesOfFood
which contains six string
values.
Accessing Array Values
You can access the values of an array with the index and square bracket notation like this:
fmt.Println(classesOfFood[0]) // Carbohydrates
fmt.Println(classesOfFood[1]) // Proteins
The code above will print the first and second values of the classesOfFood
array. The index starts from 0.
The range
Keyword
Go allows you to loop through arrays with the for
loop and the range
keyword like this:
for index, value := range classesOfFood {
fmt.Println(index, value)
}
The code above looped through the classesOfFood
array and printed out the index and value of each element:
0 Carbohydrates
1 Proteins
2 Fats and Oils
3 Vitamins
4 Minerals
5 Water
If you do not need the index or the value of the values, you can discard them with the blank identifier (_
) like this:
for _, value := range classesOfFood {
fmt.Println(value)
}
The code above will return only the values in the array.
Array Comparison
Go allows you to compare arrays with the ==
and !=
operators. For example, if you want to check whether two different arrays are of same length and contain the same values, you can do that like this:
var typesOfFood = [...]string{"Fruits", "Vegetables", "Grains", "Meats", "Dairy", "Sweets"}
var compare = classesOfFood == typesOfFood // false
The code above defines an array, typesOfFood
, and a compare
variable, which checks if both arrays are equal and stores the value. The compare
variable will return false
because even though the length of the arrays is the same, they hold different values.
Note: The array's length can be omitted by replacing it with three dots
...
as in the above code block. The compiler will infer the length based on the number of values inside the curly braces.
Multidimensional Arrays
A multidimensional array is an array that contains other arrays. You can create a multidimensional array in Go like this:
var alphs = [2][3]string{{"a", "b", "c"}, {"d", "e", "f"}}
The code above defined a multidimensional array, alphs
. The first square bracket is the length of the outer array, and the second square bracket is the length of the inner arrays. You can loop through a multidimensional array in Go like this:
for index, value := range multidimensionalArray {
fmt.Println(index, value)
}
The code above will return:
0 [a b c]
1 [d e f]
However, if you want to print out all the values inside the arrays, you can use a nested for
loop like this:
for i := 0; i < len(multidimensionalArray); i++ {
for j := 0; j < len(multidimensionalArray[i]); j++ {
fmt.Println(multidimensionalArray[i][j])
}
}
The code above will return:
a
b
c
d
e
f
Slices
Go allows you to store an array of values that you would like to add to and delete later in a collection called a slice, a dynamically-sized reference type built on top of an array. Unlike an array, a slice does not have a fixed length.
Just like an array, you can define a slice in Go like this:
var todos = []string{"Learn Go", "Learn Python", "Learn JavaScript"}
The only difference is that there's no length or ...
in the definition of a slice.
The make
Function
You can also define a slice using the make
function like this:
var groceriesList = make([]string, 10, 20)
The code above defined a slice with the make
function. The first number is the length of the slice, and the second number is the array's capacity, which is the number of values the slice can contain before being resized.
Accessing Slice Values
Just like arrays, you can access the values of a slice with the index and square bracket notation:
fmt.Println(todos[0]) // Learn Go
fmt.Println(todos[1]) // Learn Python
The code above will print the first and second values of the todos
array. The index starts from 0.
Adding Items to a Slice
Go allows you to add items to a slice with the append
slice. For example, to add new items to the todo
slice, you can do that like this:
todos = append(todos, "Learn C++")
That code above will add the new item to the end of the slice, and the todos
slice will now look like this:
[Learn Go Learn Python Learn JavaScript Learn C++]
Looping through Slices
You can loop through a slice in Go the same way you do with arrays using the for
loop and the range
keyword:
for _, todos := range todos {
fmt.Println(todos)
}
The code above looped through the todos
slice and will print out the following result:
Learn Go
Learn Python
Learn JavaScript
Learn C++
Slicing a Slice
You can extract a slice from another slice in Go with a method called slicing. For example, if you want to remove the first three items from the todo
slice:
newSlice := todos[0:3]
fmt.Println(newSlice)
The code above will return:
[Learn Go Learn Python Learn JavaScript]
You can omit the first number if you are starting from the first item like this:
newSlice := todos[:3]
fmt.Println(newSlice)
If you want to create a new slice that starts from the second item to the end, you can do that like this:
newSlice := todos[1:]
fmt.Println(newSlice)
The code above will return:
[Learn Python Learn JavaScript Learn C++]
You can copy all the items inside the todos
slice into the newSlice
slice by omitting both numbers like this:
newSlice := todos[:]
fmt.Println(newSlice)
The code above will return:
[Learn Go Learn Python Learn JavaScript Learn C++]
Removing Items from a Slice
There is no in-built function for removing items from a slice in Go. However, you can create a function that does that for you like so:
func removeTodos(slice []string, elem string) []string {
// Create a new slice to store the result
newSlice := make([]string, 0, len(slice))
// Loop over the elements in the original slice
for _, value := range slice {
// If the element is not the one to remove, add it to the new slice
if value != elem {
newSlice = append(newSlice, value)
}
}
return newSlice
}
The code above defines a function, removeTodos
that takes in a slice and the value that should be removed from the slice and can be used like this:
fmt.Println(removeTodos(todos, "Learn Go"))
The code above will remove the value "Learn Go"
from the todos
slice.
Note: the
removeTodos
function will remove all the occurrences of the value"Learn Go"
if it appears multiple times in the slice.
Copying Slices
You can also create a copy of a slice in Go with the copy
method like so:
todosCopy := make([]string, len(todos))
copy(todosCopy, todos)
fmt.Println(todosCopy)
The code above created a todosCopy
slice using the make
function and the same length as the todos
slice, then prints out the todosCopy
slice. The code above will return the values of the todos
slice as it was when it was declared like so:
[Learn Go Learn Python Learn JavaScript]
Now that you understand how to use arrays and slices in Go let's explore the significant differences between them so you can make an informed decision on when to use either of them in your code.
Differences between Arrays and Slices in Go
Arrays and slices are fundamental data types in Go, but they have major differences.
Fixed-size vs. dynamic size: Arrays have a fixed size determined at compile time, whereas slices have a dynamic size that can be changed at runtime.
Value type vs. reference type: Arrays are value types, which means that when you assign an array to a new variable or pass it as a parameter to a function, a copy of the array is created. On the other hand, Slices are reference types, which means that when you assign a slice to a new variable or pass it as a parameter to a function, a reference to the underlying array is created.
Length vs. capacity: Arrays have a fixed length determined at compile time, whereas slices have a dynamic length that can be changed at runtime. Slices also have a capacity, which is the maximum length of the slice before it needs to be resized.
In general, slices are more flexible and powerful than arrays in Go since they allow you to work with dynamic sequences of elements. However, arrays can be helpful when a fixed-size data structure is required.
Let's explore maps and how to use them in the next section.
Maps
Go allows you to define collections of key-value pairs using a map
. A map is a collection of key-value pairs where each key is unique and associated with a value. Maps are declared using the "map" keyword, followed by the key and value types in square brackets. For example:
var userIds = map[int]string{2901: "Sally", 2902: "Adeoye", 2903: "Obalola"}
The code above defines a userIds
map with integer keys, string values, and three initial items. You can also create an empty map using the following:
var foodCodes map[string]int // empty map with the map keyword
var dressCodes = make(map[string]int) // empty map with the make function
Looping Through a Map
You can loop through a map in Go using the for
loop and the range
keyword like this:
for key, value := range userIds {
fmt.Println(key, value)
}
The code above loops through the userIds
map and prints out all the keys and values:
2901 Sally
2902 Adeoye
2903 Obalola
Note: The iteration order over the key-value pairs in a map can vary between program runs.
Adding Items to Maps
You can add items to a map by assigning values to new keys like this:
userIds[2904] = "Oluwaseun"
userIds[2905] = "Oladapo"
fmt.Println(userIds)
The code above will return:
map[2901:Sally 2902:Adeoye 2903:Obalola 2904:Oluwaseun 2905:Oladapo]
Checking the Size of a Map
You can check the size of a map with the len
function like this:
fmt.Println(len(userIds)) // prints 5
The code above will return the number of key-value pairs in the userIds
map.
Deleting Items from a Map
You can delete items from a map with the delete
keyword like this:
delete(userIds, "2904")
The code above will delete the item with the 2904
key from the map.
Deleting Multiple Items from a Map
There's no in-built function for deleting multiple items from a map in Go. However, you can create a function that does that with a loop like this:
func deleteMultipleItems(m map[int]string, keys ...int) {
for _, key := range keys {
delete(m, key)
}
}
The code above defines a deleteMultipleItems
function that takes in the map and multiple keys and removes the items associated with the keys from the map. You can use the deleteMultipleItems
function with the userIds
map like so:
deleteMultipleItems(userIds, 2901, 2902, 2903)
fmt.Println(userIds)
The code above will return:
map[2904:Oluwaseun 2905:Oladapo]
Checking If an Item Exists in a Map
It's a good practice to always check if an item exists in a map before adding or deleting it. You can check if an item already exists in a map using the if
conditional statement and the ok
keyword like this:
if _, ok := userIds[2901]; ok {
fmt.Println("Key exists")
} else {
fmt.Println("Key does not exist")
}
The code above will return "Key exists"
, if the key is found and "Key does not exist"
if the given key is not in the map.
Conclusion
Arrays, slices, and maps are powerful tools that allow you to store and manipulate data differently, depending on your application needs. Understanding the differences between these collection types and when to use them can help developers write more efficient and readable code. I hope this article achieved its aim of teaching you all you need to know about collection types in Go and how to use them in your code.
In the next part of the series, we will explore what pointers and structs are in Go and how to use them.
Please leave your feedback, corrections, questions, and suggestions in the comment section, and I'll reply to all of them. Thanks!