Golang Tutorial - 5 Array, Slice and Map + slices and maps package
Tags: go, beginners, tutorial
Published: 2020-06-04 00:03:47

> **HOT UPDATE**: This post includes a tutorial on slices and maps package usage.

In programming, storing multiple values or data can be done using data structure. The data structure is a "container" that can be used to store multiple data or values. The examples of data structure in Go are array, slice, and map.

## Array
The array is a data structure that can store multiple values of the same type and use a certain size. The array isn't recommended to be used but it's good to know the characteristics of the array in Go. Because using a certain size, the size or length can't be changed automatically.

This is the illustration of an array data structure.

![Array data structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k8tmk2idzeds6vttdea7.png)

The anatomy of array declaration is like this:
```go
//[size of array]data type{values}
data := [5]int{1, 2, 3, 4, 5}
```
Adding data into an array can be done using a certain index like this:
```go
//declaring empty array
data := [2]int{}
data[0] = 1 //assign data "1" into index 0
data[1] = 2 //assign data "2" into index 1
``` 
Notice that accessing or assigning data from the beginning of the array started from index 0. 
If the number of data inserted is larger than the array's size or length. It throws an error.
For example:
```go
//declaring empty array
data := [2]int{}
data[0] = 1
data[1] = 2
data[2] = 3 //throw an error : invalid array index 2 (out of bounds for 2-element array)
```
Accessing each data from the array can be done using a for loop. 

Using regular for loop is like this:
```go
data := [5]string{"NodeJS", "PHP", "Python", "Go", "Java"} //declare an array of string
for i := 0; i < len(data); i++ {  //len(data) is used to get the length of array
	fmt.Println(data[i]) //access data from index " i "
}
```
The output :
```
NodeJS
PHP
Python
Go
Java
```
From that code, there is the `len(data)` syntax, which is a method that can be used to get the size or length of an array or slice.
Using for range loop is like this:
```go
data := [5]string{"NodeJS", "PHP", "Python", "Go", "Java"} //declare an array of string
//retrieve "v" (value) from array, ignore index using " _ " notation
for _, v := range data {
	fmt.Println(v)
}
```
The output same as the above.
Using `fmt.Println` can be used also to check the values stored inside the array.
```go
data := [5]string{"NodeJS", "PHP", "Python", "Go", "Java"} //declare an array of string
fmt.Println(data)
```
Output :
```
[NodeJS PHP Python Go Java]
```
## Slice
A slice is a data structure that works like an array but has a flexible size or length. The declaration syntax of the slice is similar to an array but size or length isn't needed.
```go
//[]data type{values}
data := []int{1, 2, 3, 4, 5}
```
The data that can be stored must have the same type like the array, the slice has a feature to select certain data that is needed. 

Slice can be created using the `make()` method. This method takes 3 arguments. The first argument is the type of slice, the second is the length of slice and the last argument is the capacity of slice.
The example is like this:
```go
//declare a slice of int using make method
//the length of slice is 3
//the capacity of slice is 6
data := make([]int, 3, 6)
//default items is based on data type's default value
// the default value of int is 0
//so this code print out [0 0 0]
fmt.Println("default items:", data)

//inserting some items
data[0] = 1
data[1] = 2
data[2] = 3
fmt.Println(data)
fmt.Println("Length of slice: ", len(data))
fmt.Println("Capacity of slice: ", cap(data))

data = append(data, 4, 5, 6) //insert new items

fmt.Println(data)
fmt.Println("Length of slice: ", len(data))
fmt.Println("Capacity of slice: ", cap(data))

//this insertion will increase the capacity 2x (two times)
data = append(data, 7)
fmt.Println("Length of slice: ", len(data))
fmt.Println("Capacity of slice: ", cap(data))
```
Based on the code above. The length of the slice defines several items that are available in the slice. The capacity of a slice defines several items that can be inserted. 

![Create a slice with make function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xj89lxuc2tz2mgimmy5q.png)

If the length of the slice is larger than the capacity. The capacity will be increased 2 times if we use the `make()` method to create a slice.


![Slice capacity update](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vuif0fvz22suvspkqcnm.png)

The declaration of multiple dimension slice is also possible. For example, we can declare a 2D slice like this:
```go
//declare a slice of []int
data := [][]int{
	{1, 2, 3},
	{4, 5, 6},
	{7, 8, 9},
}
//retrieve the items from slice using nested loops
for _, v := range data {
	for _, val := range v {
		fmt.Print(val)
	}
	fmt.Println("")
}
```
To select or slicing certain data from a slice. Use the syntax like this:
```go
//name of the slice followed by [ start index : end index]
data[1:3]
```
Here is an example, this code selects or slicing a certain value from a slice called "data" and stores it inside a new slice called "newData".
```go
data := []int{8, 9, 10, 11, 12, 13}
//selecting certain data
//name of the slice followed by [ start index : end index]
newData := data[1:3]
fmt.Println(newData)
```
The output :
```
[9 10]
```
The mechanism of slicing a data from slice looks like this :
![Slicing a slice](https://dev-to-uploads.s3.amazonaws.com/i/ft7ceuh3u5hdmvxqbc7k.png)

Another example of slicing a data in slice is like this :
* `data[1:]` means select data or values from index 1 until the last index
* `data[:4]` means select data from the first index until index 4. (data stored in index 4 isn't included).

Adding data into a slice can be done using the `append()` method. This method uses 2 inputs which is the slice and the data that need to be added. The usage is like this:
```go
//declare a slice
cars := []string{"Nissan Skyline GT-R R34", "Honda NSX", "Toyota Supra", "Mazda RX-7"}
//append new data "Subaru Impreza WRX STI 22b"
cars = append(cars, "Subaru Impreza WRX STI 22b")
```
The full example can be seen in this code:
```go 
//declare a slice
cars := []string{"Nissan Skyline GT-R R34", "Honda NSX", "Toyota Supra", "Mazda RX-7"}
fmt.Println("Before: ", cars)
//append new data "Subaru Impreza WRX STI 22b"
cars = append(cars, "Subaru Impreza WRX STI 22b")
fmt.Println("After: ", cars)
```
Output:
```
Before:  [Nissan Skyline GT-R R34 Honda NSX Toyota Supra Mazda RX-7]
After:  [Nissan Skyline GT-R R34 Honda NSX Toyota Supra Mazda RX-7 Subaru Impreza WRX STI 22b]
```
Removing certain data in a slice can be used also with `append()` with another approach. Let’s see the example:
```
Input: [12,13,14,15,16,17]
Output: [12,13,16,17] //remove 14 and 15
```
Based on that case, the steps are :
1. Take the first two items in slice {12,13}
2. Append data from index 4 until the last index {16,17} to {12,13}
The full example is like this:
```go
data := []int{12, 13, 14, 15, 16, 17}
fmt.Println("Before removed: ", data)
/**
The steps are:
1. Take the first two items in slice {12, 13}
2. Append data from index 4 until the last index {16,17} to {12,13}
**/
data = append(data[:2], data[4:]...)
fmt.Println("After removed: ", data)
```
The output:
```
Before removed:  [12 13 14 15 16 17]
After removed:  [12 13 16 17]
```
Notice that there is a `...` operator inside the `append()` method in the `data[4:]...` syntax. It means that all items are from a certain slice (this works like a spread operator in JavaScript). The `...` operator is useful to use together with the `append()` function.
If `...` isn't used, the syntax looks like this:
```go
data = append(data[:2],16,17)
```
## Map
The map is a data structure with a key-value pairs mechanism. Each item must have a unique key. The map data structure is illustrated in the picture below.


![Map Data Structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hhukt2csw9vnglprxcwu.png)

The declaration of the map in Go looks like this:
```go
//	map[key type]value type
data := map[string]string{
	"BMW":        "3.0 CSL",
	"Porsche":    "911",
	"Volkswagen": "Scirocco",
}
```
To access or retrieve an item inside a map, we can use the key that is stored.
```go
//access an item from "data" map with "BMW" as a key
data["BMW"]
```
If we try to retrieve an item with a key that isn't found inside the map, the code doesn't throw any errors. Otherwise, return an empty item. 
Adding a new item in the map can be done by defining a key and value that want to be stored inside the map. For example, we want to add a new item to the "data" map with a key and value specified like this:
```go
data["RUF"] = "CTR"
```
To retrieve all items from the map, there are 2 ways:
* Using regular `fmt.Println()`. This way is suitable for checking the items that are stored inside the map.
* Using for range loop. This way is suitable for doing any operation with items that are stored inside the map.
Here is an example.
```go
//declare a map with key type as a string and value type as a string
data := map[string]string{
	"BMW":        "3.0 CSL",
	"Porsche":    "911",
	"Volkswagen": "Scirocco",
}
//add new item into map
data["RUF"] = "CTR"
//retrieve all items from map with for range loop
for k, v := range data {
	fmt.Println("Brand: ", k, "\tCar Name: ", v)
}
```
The output.
```
Brand:  BMW     Car Name:  3.0 CSL
Brand:  Porsche         Car Name:  911
Brand:  Volkswagen      Car Name:  Scirocco
Brand:  RUF     Car Name:  CTR
```
Removing an item from the map can be done using the `delete()` method followed by the map name and item's key. The example is like this:
```go
data := map[string]string{
	"BMW":        "3.0 CSL",
	"Porsche":    "911",
	"Volkswagen": "Scirocco",
}
//remove an item inside the map called "data" with key = "Volkswagen"
delete(data, "Volkswagen")
```
The usage of multi-dimensional values is available in Map. The usage is like this:
```go
//declare a map with string as a key that stored a value with type a slice of int
scores := map[string][]int{
	"Firstname":{9,8,9},
	"Lastname":{7,6,5},
	"Surname":{8,8,8}
}
```
## slices Package

In the recent Go version (1.21+), there is a package to work with the slice data structure called `slices`. This package provides many functionalities like binary search, reverse, deletion, and others.

> In this post, some main functionalities will be covered. To learn more about the `slices` package, consider checking the [full documentation](https://pkg.go.dev/slices).

### Binary Search

Binary search is a search algorithm that can be used to search efficiently. Binary search only works with sorted elements. The `slices` package provides binary search functionality. This is an example.

```go
package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{11, 23, 45, 67, 89}

    target := 67

    // perform binary search
    idx, isFound := slices.BinarySearch(numbers, target)

    fmt.Println("index: ", idx)
    fmt.Println("is found? ", isFound)
}

```

Output

```
index:  3
is found?  true
```

Based on the code above, the `BinarySearch` function returns two values, the index of the found element and the boolean value to indicate if the element is found.

### Compact

The `Compact` function can be used to remove duplicates for sorted elements. This is an example.

```go
package main

import (
    "fmt"
    "slices"
)

func main() {
    // make sure to provide sorted elements
    foods := []string{"burger", "burger", "fried chicken", "lasagna", "lasagna"}

    // remove duplicates
    result := slices.Compact(foods)

    fmt.Println("the result: ", result)
}

```

Output

```
the result:  [burger fried chicken lasagna]
```

There is another function called `CompactFunc` to remove duplicates with a specific callback function. In this example, the duplicate elements are removed if two strings are equal while ignoring the case sensitivity.

```go
package main

import (
    "fmt"
    "slices"
    "strings"
)

func main() {
    foods := []string{"burger", "Burger", "fried chicken", "fRIeD cHICkEN", "LASAGNA", "lasagna"}

    // remove duplicates
    foods = slices.CompactFunc(foods, func(food1, food2 string) bool {
        // check if two strings are equal and ignore the case sensitivity
        return strings.EqualFold(food1, food2)
    })

    fmt.Println("result: ", foods)
}

```

Output

```
result:  [burger fried chicken LASAGNA]
```

### Contains

The `Contains` function can be used to check if a certain element exists inside a slice. This is an example.

```go
package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{45, 12, 88, 25, 100}

    target := 25

    isExist := slices.Contains(numbers, target)

    if isExist {
        fmt.Println("the value is exist")
    } else {
        fmt.Println("nope")
    }
}

```

Output

```
the value is exist
```

The `ContainsFunc` is available to check if at least one element meets the condition based on the given callback function. This is an example.

```go
package main

import (
    "fmt"
    "slices"
)

func main() {

    numbers := []int{55, 67, 18, 19}

    // check if even number is exist
    // the even number checking is provided by the callback function
    isExist := slices.ContainsFunc(numbers, func(n int) bool {
        // check if number is even
        return n%2 == 0
    })

    if isExist {
        fmt.Println("even number found")
    } else {
        fmt.Println("even number not found")
    }
}

```

Output

```
even number found
```

Based on the code above, the `ContainsFunc` is used to check if at least one element is an even number. Each number is checked by using the callback function.

### Delete

The `Delete` function is used to remove certain elements based on the given index at the start index and end index. This is an example of using the `Delete` function.

```go
package main

import (
    "fmt"
    "slices"
)

func main() {
    items := []string{"milk", "coffee", "banana", "mango", "potato"}

    fmt.Println("before delete: ", items)

    items = slices.Delete(items, 2, 4)

    fmt.Println("after delete: ", items)
}

```

Output

```
before delete:  [milk coffee banana mango potato]
after delete:  [milk coffee potato]
```

Based on the code above, the `Delete` function deletes elements from index 2 up to index 3 (because index 4 is not included). This is an illustration of how the `Delete` function works.


![Delete Function in slices](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vbzn6f0kgktsheid2oox.png)

There is another function called `DeleteFunc` to remove or filter elements based on the given callback function. This is an example to remove the empty string.

```go
package main

import (
    "fmt"
    "slices"
)

func main() {
    samples := []string{
        "",
        "this is good",
        "this is fine",
        "",
        "",
        "not bad at all",
    }

    // remove empty strings
    samples = slices.DeleteFunc(samples, func(sample string) bool {
        // check if string is empty
        return sample == ""
    })

    for _, s := range samples {
        fmt.Println(s)
    }
}


```

Output

```
this is good
this is fine
not bad at all
```

### Insert

The `Insert` function is used to insert a new element in the specific index location. This is an example.

```go
package main

import (
    "fmt"
    "slices"
)

func main() {
    items := []string{"biscuit", "cereal", "waffle"}

    // insert single item
    items = slices.Insert(items, 1, "pancake")

    // insert many items
    otherItems := []string{"milk", "oat milk"}
    items = slices.Insert(items, 2, otherItems...)

    fmt.Println("result: ", items)
}

```

Output

```
result:  [biscuit pancake milk oat milk cereal waffle]
```

Based on the code above, the `Insert` function can be used to insert a single or many elements in a slice in the specific index location. This picture describes how the `Insert` function works.


![Insert single item](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ev9ex9bwu0dmuivxy4rz.png)


![Insert multiple items](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x71ty5yqrswyqosqgrqp.png)

### Max and Min

The `Max` function can be used to get the maximum value in a slice. The `Min` function can be used to get the minimum value in a slice. This is an example.

```go
package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{44, -50, 1, 300, 2, 78}

    maxNum := slices.Max(numbers)
    minNum := slices.Min(numbers)

    fmt.Println("max number :", maxNum)
    fmt.Println("min number :", minNum)
}

```

Output

```
max number : 300
min number : -50
```

### Reverse

The `Reverse` function can be used to reverse elements in a slice. This is an example.

```go
package main

import (
    "fmt"
    "slices"
)

func main() {
    items := []string{"tea", "milk", "cereal"}

    fmt.Println("original: ", items)

    slices.Reverse(items)

    fmt.Println("after reversed: ", items)
}

```

Output

```
original:  [tea milk cereal]
after reversed:  [cereal milk tea]
```

Another example is reversing a word.

```go
package main

import (
    "fmt"
    "slices"
)

func main() {
    word := "quick"

    // get the letters from a word by converting it to the []byte
    // because string is a slice of bytes
    letters := []byte(word)

    // reverse a word
    slices.Reverse(letters)

    reversed := string(letters)

    fmt.Println("reversed word: ", reversed)
}

```

Output

```
reversed word:  kciuq
```

### Sort

The `Sort` function can be used to perform a sorting mechanism in a slice. This is an example.

```go
package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{99, 34, 45, 10, -1, 3}

    slices.Sort(numbers)

    fmt.Println("after sorted: ", numbers)
}

```

Output

```
after sorted:  [-1 3 10 34 45 99]
```

The `SortFunc` can be used to sort elements in a specific order (ascending or descending order). This is an example of `SortFunc` usage to sort elements in descending order.

```go
package main

import (
    "cmp"
    "fmt"
    "slices"
)

func main() {
    numbers := []int{99, 34, 45, 10, -1, 3}

    // sort in descending order
    slices.SortFunc(numbers, func(a, b int) int {
        return cmp.Compare(b, a)
    })

    fmt.Println("after sorted: ", numbers)
}

```

Output

```
after sorted:  [99 45 34 10 3 -1]
```

## maps Package

In the recent Go version (1.21+), there is a package to work with the map data structure called `maps`. This package provides many functionalities like filter and equality check.

> In this post, some main functionalities will be covered. To learn more about the `maps` package, consider to check the [full documentation](https://pkg.go.dev/maps).

### Delete

The `DeleteFunc` can be used to delete an item inside the map with a callback function. This is an example of using `DeleteFunc` to delete an item with a score value of less than 70.

```go
package main

import (
    "fmt"
    "maps"
)

func main() {
    scores := map[string]int{
        "bob":  79,
        "alex": 60,
        "john": 88,
        "doe":  65,
    }

    maps.DeleteFunc(scores, func(name string, score int) bool {
        return score < 70
    })

    fmt.Println("after deleted: ", scores)
}

```

Output

```
after deleted:  map[bob:79 john:88]
```

### Equal

The `Equal` function is used to check the equality of the two maps. The two maps are equal if the key and value are both equals.

```go
package main

import (
    "fmt"
    "maps"
)

func main() {
    c1 := map[string]string{
        "C1": "Algorithm",
        "C2": "Data Science",
    }

    c2 := map[string]string{
        "c1": "Algorithm",
        "c2": "Data Science",
    }

    isEqual := maps.Equal(c1, c2)

    fmt.Println("is equal: ", isEqual)
}

```

Output

```
is equal:  false
```

Based on the code above, the `Equal` returns `false` because the first map (`c1`) has uppercase characters for the key whilst the second map (`c2`) has lowercase characters for the key.

The `EqualFunc` can be used to perform an equality check with an additional callback function. The callback function is used for checking the value of the map. This is an example.

```go
package main

import (
    "fmt"
    "maps"
    "strings"
)

func main() {
    c1 := map[string]string{
        "C1": "Algorithm",
        "C2": "Data Science",
    }

    c2 := map[string]string{
        "C1": "aLgOrITHM",
        "C2": "DATA SCIENCE",
    }

    // check the equality with callback function to check the value
    isEqual := maps.EqualFunc(c1, c2, func(item1, item2 string) bool {
        return strings.EqualFold(item1, item2)
    })

    fmt.Println("is equal: ", isEqual)
}

```

Output

```
is equal:  true
```

Based on the code above, the `EqualFunc` returns `true` because there is an additional callback function to ensure two values are equal.

## Notes
* Array is rarely used in Go programming. The array can be used to store multiple values with the exact size. So the size wouldn't change.
* Slice is commonly used in Go programming. Slice can be used to store multiple values or items with enhanced features like selecting certain data. 
* Map is also commonly used. The map can be used to store multiple values with specified keys. 
* Learn more about the slices package [here](https://pkg.go.dev/slices)
*Learn more about the maps package [here](https://pkg.go.dev/maps)
* Learn more about Array, Slice, and Map [here](https://go.dev/ref/spec#Array_types)

I hope this article helps to learn the Go programming language. If you have any thoughts or feedback, you can write it in the discussion section below.