nadirbasalamah
Posted on June 4, 2020
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.
The anatomy of array declaration is like this:
//[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:
//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:
//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:
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:
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.
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.
//[]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:
//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.
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.
The declaration of multiple dimension slice is also possible. For example, we can declare a 2D slice like this:
//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:
//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".
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 :
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:
//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:
//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 :
- Take the first two items in slice {12,13}
- 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:
- Take the first two items in slice {12, 13}
- 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.
The declaration of the map in Go looks like this:
// 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.
//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:
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:
//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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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 *Learn more about the maps package here
- Learn more about Array, Slice, and Map here
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.
Posted on June 4, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.