Go Beginners Series: Collection Types, Arrays, Slices, and Maps
Tags: go, beginners, programming
Published: 2023-04-27 16:30:28

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:

```go
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:

```go
 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:

```go
 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:

```sh
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:

```go
 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:

```go
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:

```go
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:

```go
 for index, value := range multidimensionalArray {
  fmt.Println(index, value)
 }
```

The code above will return:

```sh
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:

```go
 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:

```sh
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:

```go
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:

```go
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:

```go
 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:

```go
 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:

```sh
 [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:

```go
 for _, todos := range todos {
  fmt.Println(todos)
 }
```

The code above looped through the `todos` slice and will print out the following result:

```sh
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:

```go
newSlice := todos[0:3]
fmt.Println(newSlice)
```

The code above will return:

```sh
 [Learn Go Learn Python Learn JavaScript]
```

You can omit the first number if you are starting from the first item like this:

```go
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:

```go
newSlice := todos[1:]
fmt.Println(newSlice)
```

The code above will return:

```sh
[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:

```go
newSlice := todos[:]
fmt.Println(newSlice)
```

The code above will return:

```sh
[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:

```go
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:

```go
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:

```go
 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:

```sh
[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.

1. **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.

2. **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.

3. **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:

```go
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:

```go
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:

```go
 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:

```sh
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:

```go
 userIds[2904] = "Oluwaseun"
 userIds[2905] = "Oladapo"
 fmt.Println(userIds) 
```

The code above will return:

```sh
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:

```go
 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:

```go
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:

```go
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:

```go
 deleteMultipleItems(userIds, 2901, 2902, 2903)
 fmt.Println(userIds)
```

The code above will return:

```sh
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:

```go
 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!