# Arrays, Slices and Maps

In chapter 3 we learned about Go's basic types. In this chapter we will look at three more built-in types: arrays, slices and maps.

## Arrays

An array is a numbered sequence of elements of a single type with a fixed length. In Go they look like this:

`var x int`

`x` is an example of an array which is composed of 5 `int`s. Try running the following program:

```package main

import "fmt"

func main() {
var x int
x = 100
fmt.Println(x)
}```

You should see this:

`[0 0 0 0 100]`

`x = 100` should be read “set the 5th element of the array x to 100”. It might seem strange that `x` represents the 5th element instead of the 4th but like strings, arrays are indexed starting from 0. Arrays are accessed in a similar way. We could change `fmt.Println(x)` to `fmt.Println(x)` and we would get 100.

Here's an example program that uses arrays:

```func main() {
var x float64
x = 98
x = 93
x = 77
x = 82
x = 83

var total float64 = 0
for i := 0; i < 5; i++ {
total += x[i]
}
fmt.Println(total / 5)
}```

This program computes the average of a series of test scores. If you run it you should see `86.6`. Let's walk through the program:

• First we create an array of length 5 to hold our test scores, then we fill up each element with a grade

• Next we setup a for loop to compute the total score

• Finally we divide the total score by the number of elements to find the average

This program works, but Go provides some features we can use to improve it. First these 2 parts: `i < 5` and `total / 5` should throw up a red flag for us. Say we changed the number of grades from 5 to 6. We would also need to change both of these parts. It would be better to use the length of the array instead:

```var total float64 = 0
for i := 0; i < len(x); i++ {
total += x[i]
}
fmt.Println(total / len(x))```

Go ahead and make these changes and run the program. You should get an error:

```\$ go run tmp.go
# command-line-arguments
.\tmp.go:19: invalid operation: total / 5 (mismatched types float64 and int)```

The issue here is that `len(x)` and `total` have different types. `total` is a `float64` while `len(x)` is an `int`. So we need to convert `len(x)` into a `float64`:

`fmt.Println(total / float64(len(x)))`

This is an example of a type conversion. In general to convert between types you use the type name like a function.

Another change to the program we can make is to use a special form of the for loop:

```var total float64 = 0
for i, value := range x {
total += value
}
fmt.Println(total / float64(len(x)))```

In this for loop `i` represents the current position in the array and `value` is the same as `x[i]`. We use the keyword `range` followed by the name of the variable we want to loop over.

Running this program will result in another error:

```\$ go run tmp.go
# command-line-arguments
.\tmp.go:16: i declared and not used```

The Go compiler won't allow you to create variables that you never use. Since we don't use `i` inside of our loop we need to change it to this:

```var total float64 = 0
for _, value := range x {
total += value
}
fmt.Println(total / float64(len(x)))```

A single `_` (underscore) is used to tell the compiler that we don't need this. (In this case we don't need the iterator variable)

Go also provides a shorter syntax for creating arrays:

`x := float64{ 98, 93, 77, 82, 83 }`

We no longer need to specify the type because Go can figure it out. Sometimes arrays like this can get too long to fit on one line, so Go allows you to break it up like this:

```x := float64{
98,
93,
77,
82,
83,
}```

Notice the extra trailing `,` after `83`. This is required by Go and it allows us to easily remove an element from the array by commenting out the line:

```x := float64{
98,
93,
77,
82,
// 83,
}```

## Slices

A slice is a segment of an array. Like arrays slices are indexable and have a length. Unlike arrays this length is allowed to change. Here's an example of a slice:

`var x []float64`

The only difference between this and an array is the missing length between the brackets. In this case `x` has been created with a length of `0`.

If you want to create a slice you should use the built-in `make` function:

`x := make([]float64, 5)`

This creates a slice that is associated with an underlying `float64` array of length 5. Slices are always associated with some array, and although they can never be longer than the array, they can be smaller. The `make` function also allows a 3rd parameter:

`x := make([]float64, 5, 10)`

10 represents the capacity of the underlying array which the slice points to: Another way to create slices is to use the `[low : high]` expression:

```arr := float64{1,2,3,4,5}
x := arr[0:5]```

`low` is the index of where to start the slice and `high` is the index where to end it (but not including the index itself). For example while `arr[0:5]` returns `[1,2,3,4,5]`, `arr[1:4]` returns `[2,3,4]`.

For convenience we are also allowed to omit `low`, `high` or even both `low` and `high`. `arr[0:]` is the same as `arr[0:len(arr)]`, `arr[:5]` is the same as `arr[0:5]` and `arr[:]` is the same as `arr[0:len(arr)]`.

### Slice Functions

Go includes two built-in functions to assist with slices: `append` and `copy`. Here is an example of `append`:

```func main() {
slice1 := []int{1,2,3}
slice2 := append(slice1, 4, 5)
fmt.Println(slice1, slice2)
}```

After running this program `slice1` has `[1,2,3]` and `slice2` has `[1,2,3,4,5]`. `append` creates a new slice by taking an existing slice (the first argument) and appending all the following arguments to it.

Here is an example of copy:

```func main() {
slice1 := []int{1,2,3}
slice2 := make([]int, 2)
copy(slice2, slice1)
fmt.Println(slice1, slice2)
}```

After running this program `slice1` has `[1,2,3]` and `slice2` has `[1,2]`. The contents of `slice1` are copied into `slice2`, but since `slice2` has room for only two elements only the first two elements of `slice1` are copied.

## Maps

A map is an unordered collection of key-value pairs. Also known as an associative array, a hash table or a dictionary, maps are used to look up a value by its associated key. Here's an example of a map in Go:

`var x map[string]int`

The map type is represented by the keyword `map`, followed by the key type in brackets and finally the value type. If you were to read this out loud you would say “`x` is a map of `string`s to `int`s.”

Like arrays and slices maps can be accessed using brackets. Try running the following program:

```var x map[string]int
x["key"] = 10
fmt.Println(x)```

You should see an error similar to this:

```panic: runtime error: assignment to entry in nil map

goroutine 1 [running]:
main.main()
main.go:7 +0x4d

goroutine 2 [syscall]:
created by runtime.main
t269497170/src/pkg/runtime/proc.c:221
exit status 2```

Up till now we have only seen compile-time errors. This is an example of a runtime error. As the name would imply, runtime errors happen when you run the program, while compile-time errors happen when you try to compile the program.

The problem with our program is that maps have to be initialized before they can be used. We should have written this:

```x := make(map[string]int)
x["key"] = 10
fmt.Println(x["key"])```

If you run this program you should see `10` displayed. The statement `x["key"] = 10` is similar to what we saw with arrays but the key, instead of being an integer, is a string because the map's key type is `string`. We can also create maps with a key type of `int`:

```x := make(map[int]int)
x = 10
fmt.Println(x)```

This looks very much like an array but there are a few differences. First the length of a map (found by doing `len(x)`) can change as we add new items to it. When first created it has a length of 0, after `x = 10` it has a length of 1. Second maps are not sequential. We have `x`, and with an array that would imply there must be an `x`, but maps don't have this requirement.

We can also delete items from a map using the built-in `delete` function:

`delete(x, 1)`

Let's look at an example program that uses a map:

```package main

import "fmt"

func main() {
elements := make(map[string]string)
elements["H"] = "Hydrogen"
elements["He"] = "Helium"
elements["Li"] = "Lithium"
elements["Be"] = "Beryllium"
elements["B"] = "Boron"
elements["C"] = "Carbon"
elements["N"] = "Nitrogen"
elements["O"] = "Oxygen"
elements["F"] = "Fluorine"
elements["Ne"] = "Neon"

fmt.Println(elements["Li"])
}```

`elements` is a map that represents the first 10 chemical elements indexed by their symbol. This is a very common way of using maps: as a lookup table or a dictionary. Suppose we tried to look up an element that doesn't exist:

`fmt.Println(elements["Un"])`

If you run this you should see nothing returned. Technically a map returns the zero value for the value type (which for strings is the empty string). Although we could check for the zero value in a condition (`elements["Un"] == ""`) Go provides a better way:

```name, ok := elements["Un"]
fmt.Println(name, ok)```

Accessing an element of a map can return two values instead of just one. The first value is the result of the lookup, the second tells us whether or not the lookup was successful. In Go we often see code like this:

```if name, ok := elements["Un"]; ok {
fmt.Println(name, ok)
}```

First we try to get the value from the map, then if it's successful we run the code inside of the block.

Like we saw with arrays there is also a shorter way to create maps:

```elements := map[string]string{
"H":  "Hydrogen",
"He": "Helium",
"Li": "Lithium",
"Be": "Beryllium",
"B":  "Boron",
"C":  "Carbon",
"N":  "Nitrogen",
"O":  "Oxygen",
"F":  "Fluorine",
"Ne": "Neon",
}```

Maps are also often used to store general information. Let's modify our program so that instead of just storing the name of the element we store its standard state (state at room temperature) as well:

```func main() {
elements := map[string]map[string]string{
"H": map[string]string{
"name":"Hydrogen",
"state":"gas",
},
"He": map[string]string{
"name":"Helium",
"state":"gas",
},
"Li": map[string]string{
"name":"Lithium",
"state":"solid",
},
"Be": map[string]string{
"name":"Beryllium",
"state":"solid",
},
"B":  map[string]string{
"name":"Boron",
"state":"solid",
},
"C":  map[string]string{
"name":"Carbon",
"state":"solid",
},
"N":  map[string]string{
"name":"Nitrogen",
"state":"gas",
},
"O":  map[string]string{
"name":"Oxygen",
"state":"gas",
},
"F":  map[string]string{
"name":"Fluorine",
"state":"gas",
},
"Ne":  map[string]string{
"name":"Neon",
"state":"gas",
},
}

if el, ok := elements["Li"]; ok {
fmt.Println(el["name"], el["state"])
}
}```

Notice that the type of our map has changed from `map[string]string` to `map[string]map[string]string`. We now have a map of strings to maps of strings to strings. The outer map is used as a lookup table based on the element's symbol, while the inner maps are used to store general information about the elements. Although maps are often used like this, in chapter 9 we will see a better way to store structured information.

### Problems

• How do you access the 4th element of an array or slice?

• What is the length of a slice created using: `make([]int, 3, 9)`?

• Given the array:

`x := string{"a","b","c","d","e","f"}`

what would `x[2:5]` give you?

• Write a program that finds the smallest number in this list:

```x := []int{
48,96,86,68,
57,82,63,70,
37,34,83,27,
19,97, 9,17,
}```
 ← Previous Index Next →