Arrays

Define Arrays

We use var array_name [num] data_type to define arrays in Go.

1
2
3
4
5
6
7
8
9
package main

func main() {
var arr1 [5]int //array size is required if no initial values are given
arr2 := [3]int{1, 3, 5} // can create array with initial values
arr3 := [...]int{4, 5, 6, 7, 8 ,9} // with initial values, array size is not required

fmt.Println(arr1, arr2, arr3)
}

The outputs will be

1
[0 0 0 0 0] [1 3 5] [4 5 6 7 8 9]

Define 2-D Arrays

We could define a 2-D array:

1
2
3
4
5
6
7
package main

func main() {
var grid[4][5] int;

fmt.Println(grid)
}

The output will be

1
[[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]

Iterate through arrays

for loop

Use for loop to iterate through array itemes.

1
2
3
4
5
6
7
func main() {
arr3 := [...]int{4, 5, 6, 7, 8, 9}

for i := 0; i < len(arr3); i++ {
fmt.Println(arr3[i] * 3)
}
}

The outputs will be:

1
2
3
4
5
6
12
15
18
21
24
27

Here is a very important concepts:

Functions are compressed arrays; arrays are expanded functions

for...range...

The for loop can be simplified with for...range

1
2
3
4
5
6
func main(){
arr3 := [...]int{4, 5, 6, 7, 8, 9}
for i := range arr3{
fmt.Println(arr3[i])
}
}

The outputs will be:

1
2
3
4
5
6
4
5
6
7
8
9

With for...range we could get the index, as well as the value

1
2
3
4
5
func main() {
for idx, val := range arr3 {
fmt.Println("Index is: ", idx, "---", "Value is: ", val)
}
}

The outputs will be:

1
2
3
4
5
6
Index is: 0 --- Value is: 4
Index is: 1 --- Value is: 5
Index is: 2 --- Value is: 6
Index is: 3 --- Value is: 7
Index is: 4 --- Value is: 8
Index is: 5 --- Value is: 9

Here if we just need value not index, we could use _ to ommit index

1
2
3
for _, v := range arr{
fmt.Println(v)
}

Syntax Candy range

Why do we need range? It’s both self-explanatory and elegant. Let’s compare with other languages:

  • In C/C++, there no such similar built-in features, some 3rd party libs will support this behavior
  • In Python, we have both built-in range and some powerful functions such as arange from numpy. Also in Python we could use enumerate
1
2
3
lang = ["go", "python", "c++", "javascript", "java"]
for idx, val in enumerate(lang):
print(f"Index is {idx} --- Value is {val}")

The outputs will be:

1
2
3
4
5
Index is 0 --- Value is go
Index is 1 --- Value is python
Index is 2 --- Value is c++
Index is 3 --- Value is javascript
Index is 4 --- Value is java

Go Arrays are value type

Let’s define a function called printArray

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func printArray(arr [5]int) {
for idx, val := range arr {
fmt.Println("Index is:", idx, "---", "Value is:", val)
}
}

func main() {
arr2 := [5]int{1, 3, 5, 7, 9} // can create array with initial values
printArray(arr2)
}

In printArray the parameter is arr [5]int, so the function can only take arrays of 5 items, aka [5]int is different from [10]int
Note the parameter can alternatively be arr []int, but this is a slice of array, only arr[5]int is the “true” array.

Value Type

Because arrays are value type, so when we invoke the function printArray, a copy of arr2 will be passed. We pass by the value.
If we try to change some element in the function block, it will not affect the array arr2.
Whereas in other languages such as C, the address of the first element of the array will be passed, as a result, the array elements in C could get affected.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

func printArray(arr [5]int) {
arr[0] = 100 // won't affect the element in the array
for idx, val := range arr {
fmt.Println("Index is:", idx, "---", "Value is:", val)
}
}

func main() {
arr2 := [5]int{1, 3, 5, 7, 9}
printArray(arr2)
println(arr2)
}

The output will still be:

1
2
3
4
5
6
Index is: 0 --- Value is: 100
Index is: 1 --- Value is: 3
Index is: 2 --- Value is: 5
Index is: 3 --- Value is: 7
Index is: 4 --- Value is: 9
[1 3 5 7 9]

Because invoking printArray will not change the elements in arr2.

Pointing to Array

We could change the array elements by refactor the function and pass in an Array Pointer as the function argument.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func printArrayPointer(arr *[5]int) {
arr[0] = 100
for idx, val := range arr {
fmt.Println("Index is:", idx, "---", "Value is:", val)
}
}

func main(){
fmt.Println("Calling printArray")
printArray(arr2)
fmt.Println("After Calling printArray")
fmt.Println(arr2)
fmt.Println("Calling printArrayPointer")
printArrayPointer(&arr2)
fmt.Println("After Calling printArrayPointer")
fmt.Println(arr2)
}

Unlike in C, when we invoke printArrayPointer, we need to convert the argument to an array pointer, whereas in C the array name is the address pointing to the first element of the array

The output will become:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Calling printArray
Index is: 0 --- Value is: 100
Index is: 1 --- Value is: 3
Index is: 2 --- Value is: 5
Index is: 3 --- Value is: 7
Index is: 4 --- Value is: 9
After Calling printArray
[1 3 5 7 9]
Calling printArrayPointer
Index is: 0 --- Value is: 100
Index is: 1 --- Value is: 3
Index is: 2 --- Value is: 5
Index is: 3 --- Value is: 7
Index is: 4 --- Value is: 9
After Calling printArrayPointer
[100 3 5 7 9]

To summarize

  • [5]int and [10]int are two different data types
  • Invoking func f(arr [10]int) will “copy” the array.
  • We could pass array pointers as function arguments to “mutate” array elements