This is the fourth entry of my weekly series Learning Go. Last week I covered a few common types in Go: Boolean
, Numeric
, String
, Array
, and Slice
. This week I will cover a few more pieces of Slice
, and will talk about the Map
type as well.
Pulling out values from one Slice into another is made easy in Go; this is called slicing. This is done by specifying a half-open range with two indices, separated by a colon [:]
. I will demonstrate below:
package main
import (
"fmt"
)
func main() {
x := []int{4, 5, 6, 7, 8}
y := x[1:3]
fmt.Println(y)
// [5 6]
}
x
to a Slice of the type int with the values 4 5 6 7 8
using a composite literaly
to the value of x
from the indices 1
up until but not including indices 3
5
and 6
A few things to note about slicing:
Let me give you another example
package main
import (
"fmt"
)
func main() {
x := []int{4, 5, 6, 7, 8}
fmt.Println(x[:4])
// [4 5 6 7]
}
Above we are slicing a Slice; however, in this example we omit the first indices.
As you can see, when we omit the first indices it will default to 0
, and if we were to omit the second indices it would default to the length of the Slice.
The result is taking all values in the Slice until the fourth indices.
Adding more values into a Slice in Go is made easy by using the built-in variadic function, append
. Quick note: a variadic function is a function that can take zero, or any number of trailing arguments. Let’s see an example of a variadic function first:
package main
import (
"fmt"
)
func count(x ...int) {
for _, v := range x {
fmt.Println("The current value is: ", v)
}
}
func main() {
n := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
count(n...)
}
count
count
has a single parameter x
count
function variadic is the ...
syntax after the x
parameterx
main
function you will see that we use the ...
syntax when we call the count
functioncount
...
syntax we are letting the Go compiler know that there could be 10 items in this slice, or 100 itemsAs mentioned above, using the append
function is a quick and easy way to add values to a Slice in Go. The append
function takes the original Slice as the first argument, and the values as the second argument. The second argument can of course be an arbitrary amount since it is a variadic function. The append
function returns a Slice of the same type.
package main
import (
"fmt"
)
func main() {
n := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
n = append(n, 11, 12, 13, 14, 15)
fmt.Println(n)
// [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
}
main
function, we declare the variable x
and set the value to a Slice of type int
that contains 1
through 10
n
to the return value of the append
functionappend
function we pass n
as the first argument - note this is a Slice of type int
11
through 15
Let’s see what happens if you try to use two different types when calling an append
function.
package main
import (
"fmt"
)
func main() {
n := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
n = append(n, "not", "an", "int")
fmt.Println(n)
// cannot use "not" (type untyped string) as type int in append
}
append
were not the type int
the Go compiler throws an error and displays this message:cannot use "not" (type untyped string) as type int in append
Go makes it easy for you to write code that will not produce inconsistent types.
Go makes removing values from a Slice very intuitive. We will also be using slicing when we want to remove items from a Slice.
package main
import (
"fmt"
)
func main() {
n := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
n = append(n, 11, 12, 13, 14, 15)
fmt.Println(n)
// [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
n = append(n[:3], n[4:]...)
fmt.Println(n)
// [1 2 3 5 6 7 8 9 10 11 12 13 14 15]
}
main
function, we declare the variable n
and set the value to a Slice of type int
with the values 1
through 10
append
the values 11
, 12
, 13
, 14
, and 15
to the Slice n
4
from n
[:]
n
up until the third indices n[:3]
n
n[4:]
:
we take all of the rest of the values in n
...
syntax again, this is to ensure we can take an arbitrary amount of arguments in case the size of n
growsunordered group of elements of the same type, indexed by a set of unique keys of another type - called the “key type”
A few things to note about Map:
key
value
storeLet’s see a few common uses of Map in action:
package main
import (
"fmt"
)
func main() {
m := map[string]int{
"yoda": 900,
"obi wan": 34,
}
fmt.Println(m)
// map[yoda:900, obi wan: 34]
}
main
function, we declare the variable m
that is assigned to a map
map
’s key
will be of type string
map
’s value
will be of type int
A cool thing about Map is if you try to access a value by key
and it does not exist, it will still return a zero value
. This can be very useful in preventing an error
being thrown for Map lookups. Speaking of, looking up a value in a Map via the key
is super straight forward:
package main
import (
"fmt"
)
func main() {
m := map[string]int{
"yoda": 900,
"obi wan": 34,
}
fmt.Println(m["yoda"])
// 900
}
As mentioned earlier, lookups in Maps are very quick and efficient. This is because when Go is given the explicit key
to a value
there is not any outstanding time or space complexity.
Above, we grab the value of yoda
simply by using bracket notation and passing the key
"yoda"
.
What if a specified key is not found? I will show you a quick and simple way to handle this case, called the Comma Ok Idiom.
package main
import (
"fmt"
)
func main() {
m := map[string]int{
"yoda": 900,
"obi wan": 34,
}
if v, ok := m["yoda"]; ok {
fmt.Println("found yoda's age, you have", v)
// found yoda's age, you have 900
}
}
The Comma Ok Idiom allows you to write defensive code in just a few lines.
if
statement that has two return values, v
(value) and ok
(condition is evaluated to true
)key
"yoda"
we step into this block and print this statement, along with the value
of the "yoda"
key
Go makes adding elements to a Map easy peasy. Let me show you how it is done:
package main
import (
"fmt"
)
func main() {
m := map[string]int{
"yoda": 900,
"obi wan": 34,
}
// adds element to a map
m["darth vader"] = 24
fmt.Println(m)
// map[darth vader:24 obi wan:34 yoda:900]
}
To add a key
value
pair to a Map, you simply follow this syntax m["key"] = value
As seen above, we add the key
"darth vader"
and the value
24
to our Map m
Looping in Maps is very common, just as they are in Arrays or Slices. In Maps, they are just as easy as well.
package main
import (
"fmt"
)
func main() {
m := map[string]int{
"yoda": 900,
"obi wan": 34,
}
// loop over map and print key value pairs
for k, v := range m {
fmt.Println(k, v)
// yoda 900
// obi wan 34
// darth vader 24
}
}
As you can see, when looping over a Map, the syntax to do so is very similar to looping over an Array or Slice. The main difference here is the use of k
(key) instead of i
(index). This is due to the structural differences of Map.
Sometimes something just has to go. Luckily this action is accomplished painlessly.
package main
import (
"fmt"
)
func main() {
m := map[string]int{
"yoda": 900,
"obi wan": 34,
}
delete(m, "yoda")
fmt.Println(m)
// map[obi wan: 34]
}
delete
function is built into the Map type, and as you can see it is very easy to usedelete
takes two arguments: the Map that you want to remove something from, and the key
of the entry that you wish to removeIt isn’t a bad idea to use the Comma Ok Idiom when wanting to remove items from a Map conditionally as well:
package main
import (
"fmt"
)
func main() {
m := map[string]int{
"yoda": 900,
"obi wan": 34,
}
if _, ok := m["yoda"]; ok {
delete(m, "yoda")
}
fmt.Println(m)
// map[obi wan:34]
}
Much like in the last example of using the Comma Ok Idiom, we create an if
statement that has two return values, the value
of the key
and a bool
value once the expression is evaluated.
ok
will evaluate to true
value
) because we don’t need it in this exerciseok
evaluated to true
, we step into this if
block and call the delete
functionm
as the first argument, and the key
"yoda"
as the second argumentm
and see that the key
value
pair for yoda
and 900
has been removedThe more I explore the fundamentals of these common types, the more excited I get. I love that Go makes common actions of these types so easy (adding, removing, shifting, etc). Next week I will cover two more data types I have used that are of equal importance: Structs and Interfaces. See you then!