Go - Use data types and structs, arrays, slices, and maps in Go
Source: Use data types and structs, arrays, slices, and maps in Go
Arrays
To declare an array in Go, you have to define the data type of its elements and the number of elements the array can hold.
// declare and array of 4 integers (length fixed)
var a [3]int
// populate it
a[1] = 10
// declare and initialize it
cities := [5]string{"New York", "Paris", "Berlin", "Madrid"}
// The array length was determined by the strings you put when you initialized it.
// You're not reserving memory you don't know if you'll end up needing.
fmt.Println("Cities:", cities) // Cities: [New York Paris Berlin Madrid]
// The array length was determined by the strings you put when you initialized it. You're not reserving memory you don't know if you'll end up needing.
numbers := [...]int{99: -1}
fmt.Println("First Position:", numbers[0]) // First Position: 0
fmt.Println("Last Position:", numbers[99]) // Last Position: -1
fmt.Println("Length:", len(numbers)) // Length: 100
Multidimensional arrays
E.g: a two-dimensional array
var twoD [3][5]int // Here the two dimensional array: 3 rows and 5 columns
for i := 0; i < 3; i++ {
for j := 0; j < 5; j++ {
twoD[i][j] = (i + 1) * (j + 1)
}
fmt.Println("Row", i, twoD[i])
}
fmt.Println("\nAll at once:", twoD)
// Row 0 [1 2 3 4 5]
// Row 1 [2 4 6 8 10]
// Row 2 [3 6 9 12 15]
// All at once: [[1 2 3 4 5] [2 4 6 8 10] [3 6 9 12 15]]
Slices
A slice is a data structure on top of an array or another slice. With a slice, you can access the whole underlying array or only a subsequence of elements.
A slice has three components:
- Pointer to the first reachable element of the underlying array:
array[0]
- Length of the slice. The number of elements in the slice
- Capacity of the slice. The number of elements between the start of the slice and the end of the underlying array.
Declare and initialize a slice
months := []string{"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
fmt.Println(months) // [January February March April May June July August September October November December]
fmt.Println("Length:", len(months)) // Length: 12
fmt.Println("Capacity:", cap(months)) // Capacity: 12
Slice items
Go allows the slice operator s[i:p]
:
s
is the slicei
is the index of the first element to be included in the slicep
is the index of the first element to be excluded from the slice
months := []string{"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
quarter1 := months[0:3] // [January February March]
fmt.Println(quarter1, len(quarter1), cap(quarter1)) // [January February March] 3 12
quarter2 := months[3:6] // [April May June]
fmt.Println(quarter2, len(quarter2), cap(quarter2)) // [April May June] 3 9
Notice how the length of the slices is the same, but the capacity is different. Let’s explore the quarter2
slice. When you declare this slice, you’re saying you want the slice to start at position number three, and the last element is located at position number six. The slice’s length is three elements, but the capacity is nine because the underlying array has more elements or positions available but not visible to the slice.
The capacity of a slice only tells you how much you can extend a slice. You could create an extended slice from quarter2
months := []string{"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
quarter2 := months[3:6]
fmt.Println(quarter2, len(quarter2), cap(quarter2)) // [April May June] 3 9
quarter2Extended := quarter2[:4]
fmt.Println(quarter2Extended, len(quarter2Extended), cap(quarter2Extended)) // [April May June July] 4 9
([:4])
: Go assumes that you want the first position of the slice[1:]
: Go assumes that you want the last position of the slice
Append items
Go offers the append(slice, element)
built-in function. The append function then returns a new slice that you store in a variable. It could be the same variable for the slice you’re changing.
var numbers []int
numbers = append(numbers, 2) // [2]
numbers = append(numbers, 3, 4, 5) // [2 3 4 5]
Remove items
You can use the slice operator s[i:p]
to remove items from a slice.
letters := []string{"A", "B", "C", "D", "E"} // [A B C D E]
letters = append(letters[:2], letters[2+1:]...) // [A B D E]
Create copies of slices
Go has a built-in copy(dst, src []Type)
function to create copies of a slice.
slice2 := make([]string, 3)
copy(slice2, letters[1:4])
Loop in slices
If we want to update the value of each element in a slice, we can use a for loop:
type MyType struct {
field string
}
var array [10]MyType
for idx, _ := range array {
array[idx].field = "foo"
}
Maps
A map in Go is basically a hash table, which is a collection of key and value pairs
Declare and initialize a map
map[key_type]value_type
, where key_type
is the type of the keys and value_type
is the type of the values.
studentsAge := map[string]int{
"john": 32,
"bob": 31,
}
fmt.Println(studentsAge) // map[john:32 bob:31]
If you don’t want to initialize a map with items, use the make()
function to create an empty map.
studentsAge := make(map[string]int)
Add items
You only need to define a key and a value. If the pair doesn’t exist, the item is added to the map
studentsAge := make(map[string]int)
studentsAge["john"] = 32
studentsAge["bob"] = 31
fmt.Println(studentsAge) // map[john:32 bob:31]
If you create a nil
map, you get an error adding values to it.
Access items
Usual subscript notation m[key]
.
If the key isn’t present on a map, the value returned is zero value
for the map’s value type.
studentsAge := make(map[string]int)
studentsAge["bob"] = 31
fmt.Println("Bob's age is", studentsAge["bob"]) // Bob's age is 31
fmt.Println("Christy's age is", studentsAge["christy"]) // Christy's age is 0
It’s fair that Go doesn’t return an error when you access an item that doesn’t exist in a map. But there are times where you need to know if an item exists or not
studentsAge := make(map[string]int)
studentsAge["john"] = 32
age, exist := studentsAge["christy"]
if exist {
fmt.Println("Christy's age is", age) // Christy's age is 0
} else {
fmt.Println("Christy's age is not defined") // Christy's age is not defined
}
Remove items
Use the built-in delete()
function. If you try to delete an item that doesn’t exist, Go won’t panic
studentsAge := make(map[string]int)
studentsAge["john"] = 32
studentsAge["bob"] = 31
delete(studentsAge, "john")
fmt.Println(studentsAge) // map[bob:31]
delete(studentsAge, "christy")
fmt.Println(studentsAge) // map[bob:31]
Loop in a map
Use the range
-based loop. Range
yields first the key of an item, and secondly the value of that item. You can ignore either one of them by using the _
variable.
for name, age := range studentsAge {
fmt.Printf("%s\t%d\n", name, age)
}
Structs
A struct is a collection of fields in one structure
Declare and initialize a struct
Use the struct
keyword, along with the list of fields and their type. To access individual fields of a struct, you can do it by using the dot notation (.)
type Employee struct {
ID int
FirstName string
LastName string
Address string
}
// Declare a variable
var john Employee
// Declare and initialize it
employee := Employee{1001, "John", "Doe", "Doe's Street"}
employee := Employee{LastName: "Doe", FirstName: "John"}
// access to each individual field
employee.ID = 1001
fmt.Println(employee.FirstName) // John
Use the &
operator to yield a pointer to the struct.
employee := Employee{LastName: "Doe", FirstName: "John"}
fmt.Println(employee) // {John Doe }
employeeCopy := &employee
employeeCopy.FirstName = "David"
fmt.Println(employee) // {0 David Doe }
Struct embedding
Embed another struct within a struct to reduce repetition and reuse a common struct.
type Person struct {
ID int
FirstName string
LastName string
Address string
}
type Employee struct {
Information Person
ManagerID int
}
var employee Employee
employee.Information.FirstName = "John"
Instead of this (it’s not a good practice), you can simply include a new field with the same name of the struct you’re embedding:
type Person struct {
ID int
FirstName string
LastName string
Address string
}
type Employee struct {
Person // Embed the Person struct
ManagerID int
}
var employee Employee
employee.FirstName = "John"
Encode and decode structs with JSON
You can use structs to encode and decode data in JSON
To encode a struct into JSON, you use the json.Marshal
function. And to decode a JSON string into a data structure, you use the json.Unmarshal
function.
// To add a good example