What is an array?
How to create an array.
How to create multidimensional arrays.
How to iterate over an array.
How to find an element in an array.
array
element type
length
capacity
dimension of arrays
An array is a collection of elements of the same type.
The type of elements inside an array is called the Element Type
This collection has a fixed number of elementsknown at compile time.
The
Let’s say that you want to store month’s name. You can use an array of strings. The number of months is known at compile time (12)
\n\nHere is an example of how to define an array in a go program :
\n// array/creation/main.go\npackage main\n\nimport "fmt"\n\nfunc main() {\n var myArray [2]int\n myArray[0] = 156\n myArray[1] = 147\n fmt.Println(myArray)\n // output : [156 147]\n}
\nThe previous program define at the first line a variable called myArray
which is an array of integers (int
) and that has a length of 2.
In other words, this is a collection of two elements of type int
.
We then set the first element with the expression myArray[0]
. 0 represents the index of the first element of myArray
. The elements are indexed from 0.
After that, we attribute to the second element of the array the value 147. An then, we print our array.
\nUsing the previous method to define an array is correct but not very convenient to write. We used three lines to define the array. There is an easier method: using an array literal.
\nmyArray := [2]int{156,147}\nfmt.Println(myArray)\n// output : [156 147]
\nIn the previous lines of code, we define an array of 2 integers and directly populate it by using the array literal [n]T{}
You can also let the compiler infer the size of our array by using the syntax [...]T{}
. For instance :
myArray := [...]int{156, 147}
\n\n// long way\nvar a [2]int\na[0] = 156\na[1] = 147\nfmt.Println(a)\n\n// more concise\nb := [2]string{"FR", "US"}\nfmt.Println(b)\n\n// size computed by the compiler\nc := [...]float64{13.2, 37.2}\nfmt.Println(c)\n\n// values not set (yet)\nd := [2]int{}\nfmt.Println(d)
\n\nWhen you define an array, you do not have to know all the values you want to put in it.
\nYou can create an empty array and fill it during the program execution. But remember there are no undefined elements in Go.
\nWhen you define an empty array go will fill it with the zero value of the element type:
\n// array/zero-value/main.go\npackage main\n\nimport "log"\n\nfunc main() {\n var myA [3]int\n log.Printf("%v", myA)\n}
\nWill output :
\n2021/01/27 17:56:21 [0 0 0]
\nWe define here an array of integers with a length of 3. Because we do not specify any value inside the curly brackets go fills it with the zero value of integers, which is “0”. Note that the behavior is the same if you define an array of strings without specifying the values :
\nmyEmptyArray2 := [2]string{}\nfmt.Println(myEmptyArray2)\n// output : []
\nGo will put an empty string into the first element of the collection myEmptyArray2[0]
. and in the second element of the collection myEmptyArray2[1]
.
len
is a Go builtin function that will return the length of a variable v. The length of an array is the number of elements inside it. Let’s denote v an array of type T then the length is :
len(v)
\n\nThe cap
built-in function returns the same as the len
function. It is not useful in the context of arrays but will be very convenient when manipulating slices.
Arrays are a convenient and efficient way to store data with a length known at compile time. But if we store something, it’s for reading it later. When you want to read a particular element of an array, you can access it directly by its index :
\n// array/access-element/main.go\npackage main\n\nimport "fmt"\n\nfunc main() {\n b := [2]string{"FR", "US"}\n firstElement := b[0]\n secondElement := b[1]\n fmt.Println(firstElement, secondElement)\n}
\nIn this example, we access the second element of the array b
by using the notation b[1]
.
You have to be sure that the index you provide (here 1) is defined in the array. If you want to get the 101st element of the array you will not be able to compile your program. You are trying to access the value of something that does not exist. Here is the compiler gives the error:
\ninvalid array index 100 (out of bounds for 2-element array)
\n\nThe for loop allow you to scan all the elements of an array iteratively :
\nmyArray := [2]int{156, 147}\nfor index, element := range myArray {\n fmt.Printf("element at index %d is %d\\n", index, element)\n}\n// output :\n//element at index 0 is 156\n//element at index 1 is 147
\nThe for
loop in combination with the keyword range
is used to iterate over the elements of an array.
You will get the current index at each iteration with the following syntax.i
and the element e
.
Sometimes you are just interested in the value, not the indexes. You can use the following syntax to ignore the index :
\nfor _, element := range myArray {\n fmt.Println(element)\n}
\n_
is the blank identifier. Thanks to this identifier, the index is ignored by the program. Note that you can do the same for the value :
for index, _ := range myArray {\n fmt.Println(index)\n}
\nThis use case is rare.
\n\nThe previous method is interesting if you want to start from the beginning iteration. But what if you want to start from a specific index and stops at another one. Let’s say I have a table with five elements, and I want to start from the element at index two and stop at the element at index 4 (the fifth element)?
\nWe can use the for loop with a variable that will serve as a counter.
\nmyArray2 := [2]int{156, 147}\nfor i := 0; i < len(myArray2); i++ {\n fmt.Printf("element at index %d is %d\\n", i, myArray2[i])\n}\n// output :\n//element at index 0 is 156\n//element at index 1 is 147
\nHere is the syntax definition :
\nTo iterate over the array from index 0 to the last index, use the following syntax :
\nfor i := 0; i < len(a); i++ {\n fmt.Println(i, a[i])\n}
\nTo iterate in descending order (from the last element till to the first) :
\nfor i := len(a) - 1; i >= 0; i-- {\n fmt.Println(i, a[i])\n}
\n\nTo find an element inside an array, you have no other choice to check every element of it :
\n// getIndex will find an element (needle) inside an array (haystack)\n// if not found the function returns -1\nfunc getIndex(haystack [10]int, needle int) int {\n for index, element := range haystack {\n if element == needle {\n return index\n }\n }\n return -1\n}
\nThis function is not perfect. Imagine that the element you search is at the end of the array. Your function will iterate through each array’s element before finding the index.
\nAnother drawback: this function is specific for arrays of 10 elements!
\n\nYou can compare two arrays that have :
\nexactly the same type of element (for instance, integers)
exactly the same length.
And the only comparison operators that are authorized for arrays are ==
(equal) and !=
(not equal) :
a := [2]int{1, 2}\nb := [2]int{1, 2}\nif a == b {\n print("equal")\n} else {\n print("not equal")\n}\n\n// output : equal
\n\nThis is an easy mistake: passing an array to a function will copy the array.
When you want to modify the array in the function pass a pointer to the array
Attention! If your array is large making copies of it can impact the performance of your program.
Here is an example package :
\n// array/demo/demo.go\npackage demo\n\nconst NewValue = "changedValue"\n\nfunc UpdateArray1(array [2]string) {\n array[0] = NewValue\n}
\nThe function UpdateArray1
takes a parameter of type [2]string
. The function will modify the value at index 0. Let’s test this :
// array/demo/demo_test.go\npackage demo\n\nimport (\n "testing"\n\n "github.com/stretchr/testify/assert"\n)\n\nfunc TestUpdateArray1(t *testing.T) {\n testArray := [2]string{"Value1", "Value2"}\n UpdateArray1(testArray)\n assert.Equal(t, NewValue, testArray[0])\n}
\nIn this unit test we create a new array of length 2 testArray
.
We launch the function UpdateArray1
. After the function call, we verify that the element at index 0 is equal to the constant NewValue
. Let’s run this test :
$ go test ./...\n--- FAIL: TestUpdateArray1 (0.00s)\n demo_test.go:11:\n Error Trace: demo_test.go:11\n Error: Not equal:\n expected: "changedValue"\n actual : "Value1"\n\n Diff:\n --- Expected\n +++ Actual\n @@ -1 +1 @@\n -changedValue\n +Value1\n Test: TestUpdateArray1\nFAIL\nFAIL maximilien-andile.com/array/copy/demo 0.016s\nFAIL
\nThe function UpdateArray1 will receive a copy testArray
. The function modifies the copy, not the original array.
To modify the original array, the function should take an element of type*[2]string
. This is a pointer type. It signals that the function can take any pointer to values of type [2]string
(which is called the base type). A pointer is an address that points to a space in memory.
// array/demo/demo.go \npackage demo\n\nconst NewValue = "changedValue"\n\nfunc UpdateArray2(array *[2]string) {\n array[0] = NewValue\n}
\nThis is the second version of the function that now accepts a pointer type. Let’s test that this function is working :
\n// array/demo/demo_test.go \npackage demo\n\nimport (\n "testing"\n\n "github.com/stretchr/testify/assert"\n)\n\nfunc TestUpdateArray2(t *testing.T) {\n testArray := [2]string{"Value1", "Value2"}\n UpdateArray2(&testArray)\n assert.Equal(t, NewValue, testArray[0])\n}
\nWe create a new array of strings testArray
. We initialize the two array values. After the function call, we test that the element at index 0 is equal to the constant NewValue
. Let’s run the test :
$ go test ./...\nok maximilien-andile.com/array/copy/demo 0.015s
\n\nWhat will be the result of this unit test?
\n// array/demo/demo_test.go \nfunc TestArrayCopy(t *testing.T) {\n testArray := [2]string{"Value1", "Value2"}\n newCopy := testArray\n testArray[1] = "updated"\n assert.Equal(t, "updated", newCopy[1])\n}
\nThis test does not pass. The value of newCopy[1]
is equal to \"Value 2\"
. Why? That’s because when we create the variable newCopy
the array testArray
will be copied. In memory, we will have two separate arrays. When you modify testArray
you do not modify newCopy
.
In the following test, we do not copy the array, but we create a new variable of type *[2]string
(pointer to an array of 2 strings) :
// array/demo/demo_test.go \nfunc TestArrayReference(t *testing.T) {\n testArray := [2]string{"Value1", "Value2"}\n reference := &testArray\n testArray[1] = "updated"\n assert.Equal(t, "updated", reference[1])\n}
\nreference
is a pointer to the original array testArray
. When we write reference[1]
internally Go will fetch the value stored into testArray[1]
.
The term “dimension” might generate anxiety in those who hate mathematics. You should not be afraid; the concept behind is very simple. To understand it, I recommend visualizing it. On figure 2 you can see a one dimension array (length is 3, and the elements are of type int).
\nIn the previous example, we stored integers into our array. When you store strings, integers, floats, booleans, bytes, complex numbers into an array, the dimension of this array is one.
\nWhen the values of our array is another array, we have a multidimensional array. You can see a two-dimensional array in figure 3.
\nThe type of such an array is :
\n[3][2]int
\nThe array is composed of 3 values. Those three values are of type [2]int
. This is an array into an array.
To access a particular element in such an array, you can use the following syntax :
\nvalue := a[2][1]
\nmyArr[2]
will fetch the value at index 2. The value at index 2 is an array. [1]
will fetch the element at index 1 in this array.
In our hotel, the staff needs to have the ability to get the list of guests. Our hotel is composed of 10 rooms, and each room can have a maximum of two guests. To represent this guest list, we can use a two-dimensional array.
\nThe index of the first array is the room number. There are ten rooms, so the length of this array is 10. Inside each element, we put another array composed of two strings ([2]string
). Here is the code that will generate this list :
// array/two-dimensional/guestList/guestList.go\npackage guestList\n\nimport (\n "github.com/Pallinder/go-randomdata"\n)\n\nfunc generate() [10][2]string {\n guestList := [10][2]string{}\n for index, _ := range guestList {\n guestList[index] = roomGuests(index)\n }\n return guestList\n}\n\nfunc roomGuests(roomId int) [2]string {\n guests := [2]string{}\n guests[0] = randomdata.SillyName()\n guests[1] = randomdata.SillyName()\n return guests\n}
\nIn the function generate
we will initialize our two-dimensional array. Then we iterate over it with a for-range loop. For each element, we put an element of type [2]string
(An array of length 2 composed of two strings). This array contains the two guest names.randomdata.SillyName()
is used to generate fun names. In a real-life system, we will fetch those names in a database.
If we print this array, we get the following result :
\n[[Cockatoorogue Liftersolstice] [Footclever Chargernickel] [Chestsatin Scarivy] [Jesterbush Cloaksteel] [Robincanyon Boltchip] [Bonefrill Shiftshy] [Skinnerflannel Looncalico] [Loonnova Spikemesquite] [Hideforest Yakstitch] [Cloakcoconut Minnowsnapdragon]]
\nTo access the name of the second guest sleeping in room 7, we can use the syntax: guestList[7][1]
.
It’s possible in Go to generate arrays that have more than two dimensions :
\n// array/two-dimensional/main.go\npackage main\n\nimport "fmt"\n\nfunc main() {\n threeD := [2][2][2]string{}\n threeD[0][0][0] = "element 0,0,0"\n threeD[0][1][0] = "element 0,1,0"\n threeD[0][0][1] = "element 0,1,1"\n fmt.Println(threeD)\n}
\nThe array 3d
has three dimensions. On figure 6 you can visualize its shape.
Arrays that have more than two dimensions are hard to visualize. If you want to introduce such a type of array in your code, maybe you should consider another solution. Your code will be harder to read and harder to understand and review.
\n\nAccessing data on an array with its index is a very fast operation.
Finding that an element is inside an array can be slow because you have to iterate over it. If you have no clue about the order of the elements stored in your array you will probably need to iterate over all the array. In this case, you can maybe use a map (see the dedicated chapter).
Arrays are efficient, but their main limitation is their fixed size.
\nIn a real-world situation, data that you want to store rarely have a size fixed at compile time. Therefore the Go creators have created the notion of slices. We will see how to manipulate them in a dedicated chapter.
\n\nAn array is a collection of elements with various types. True or False?
An array is passed as a parameter to a function; can the function modify the array?
What is the length of an array?
What is the relation between length and capacity (for arrays)?
An array is a collection of elements with various types. True or False?
\nFalse
The elements of an array should have the same type.
An array is passed as a parameter to a function; can the function modify the array?
\nNo
Hiwever if you pass a pointer to an array to the function, it can modify it.
What is the length of an array?
\nWhat is the relation between length and capacity (for arrays)?
\nAn array is a collection of elements of the same type
The size of an array is called the length.
The length of an array is known at compile time.
In other words, an array once created cannot grow
To iterate over an array, you can use a for loop (with or without a range clause)
When an array is passed to a function, it is copied, the function cannot modify it
When you pass to a function a pointer to an array, you make the function able to modify the array.
There is no built-in function to find an element in an array.
Previous
\n\t\t\t\t\t\t\t\t\tUnit Tests
\n\t\t\t\t\t\t\t\tNext
\n\t\t\t\t\t\t\t\t\tSlices
\n\t\t\t\t\t\t\t\t