Golang: Zero to Map (Part 1 of a Zero to Hero Series)
Sep 20, 2022 - ⧖ 10.0 minIntroduction
Golang is a language created by Google to be performative and easy to use and write.
I’ve been in contact with this language for the past couple of months, and I figured out that the best way to fix the knowledge will be helping other people to improve their Golang skills. In this article, we’ll learn the basics about Go, like types, variables, functions, advanced data structures, and how to iterate them.
I hope that this article can be helpful to you.
Let’s start talking about Golang types.
Types in Golang
Variables and constants
Variables can be declared using the keyword var or the short assignment statement:=.
// In Go, _variables_ are explicitly declared and used by
// the compiler to e.g. check type-correctness of function
// calls.
package main
import "fmt"
func main () {
// `var` declares 1 or more variables.
var a = "initial"
fmt .Println (a )
// You can declare multiple variables at once.
var b , c int = 1 , 2
fmt .Println (b , c )
// Go will infer the type of initialized variables.
var d = true
fmt .Println (d )
// Variables declared without a corresponding
// initialization are _zero-valued_. For example, the
// zero value for an `int` is `0`.
var e int
fmt .Println (e )
// The `:=` syntax is shorthand for declaring and
// initializing a variable, e.g. for
// `var f string = "apple"` in this case.
f := "apple"
fmt .Println (f )
}
Constants can be declared using the keyword const, followed by the variable's name and type.
// Go supports _constants_ of character, string, boolean,
// and numeric values.
package main
import (
"fmt"
)
// `const` declares a constant value.
const s string = "constant"
func main () {
fmt .Println (s )
// A `const` statement can appear anywhere a `var`
// statement can.
const n = 500000000
// Constant expressions perform arithmetic with
// arbitrary precision.
const d = 3e20 / n
fmt .Println (d )
// A numeric constant has no type until it's given
// one, such as by an explicit conversion.
fmt .Println (int64 (d ))
}
Zero values
Zero values are:
Example
package main
import "fmt"
func main () {
var i int
var f float64
var b bool
var s string
fmt .Printf ("%v %v %v %q\n" , i , f , b , s )
}
Loops
You can use the classic for loop:
Golang doesn’t have a while keyword, so to create while loops, you can do the:
You can also create an infinite loop:
Example
// `for` is Go's only looping construct. Here are
// some basic types of `for` loops.
package main
import "fmt"
func main () {
// The most basic type, with a single condition.
i := 1
for i <= 3 {
fmt .Println (i )
i = i + 1
}
// A classic initial/condition/after `for` loop.
for j := 7 ; j <= 9 ; j ++ {
fmt .Println (j )
}
// `for` without a condition will loop repeatedly
// until you `break` out of the loop or `return` from
// the enclosing function.
for {
fmt .Println ("loop" )
break
}
// You can also `continue` to the next iteration of
// the loop.
for n := 0 ; n <= 5 ; n ++ {
if n % 2 == 0 {
continue
}
fmt .Println (n )
}
}
If Else
It’s like other languages
Example
// Branching with `if` and `else` in Go is
// straight-forward.
package main
import "fmt"
func main () {
// Here's a basic example.
if 7 % 2 == 0 {
fmt .Println ("7 is even" )
} else {
fmt .Println ("7 is odd" )
}
// You can have an `if` statement without an else.
if 8 % 4 == 0 {
fmt .Println ("8 is divisible by 4" )
}
// A statement can precede conditionals; any variables
// declared in this statement are available in all
// branches.
if num := 9 ; num < 0 {
fmt .Println (num , "is negative" )
} else if num < 10 {
fmt .Println (num , "has 1 digit" )
} else {
fmt .Println (num , "has multiple digits" )
}
}
// Note that you don't need parentheses around conditions
// in Go, but that the braces are required.
Conditional Statement — Switch
Unlike other languages (like JavaScript, Java, and C), on Go, the executed that will case on the switch statement is the selected case (so, you don’t need to use the break keyword to stop the execution. This will happen automatically). Switch cases execute from top to bottom, and the execution will stop when one case succeeds.
Switches without conditions are the same that a switch with true. It’s cleaner than using a bunch of if-then-else.
Example
// _Switch statements_ express conditionals across many
// branches.
package main
import (
"fmt"
"time"
)
func main () {
// Here's a basic `switch`.
i := 2
fmt .Print ("Write " , i , " as " )
switch i {
case 1 :
fmt .Println ("one" )
case 2 :
fmt .Println ("two" )
case 3 :
fmt .Println ("three" )
}
// You can use commas to separate multiple expressions
// in the same `case` statement. We use the optional
// `default` case in this example as well.
switch time .Now ().Weekday () {
case time .Saturday , time .Sunday :
fmt .Println ("It's the weekend" )
default :
fmt .Println ("It's a weekday" )
}
// `switch` without an expression is an alternate way
// to express if/else logic. Here we also show how the
// `case` expressions can be non-constants.
t := time .Now ()
switch {
case t .Hour () < 12 :
fmt .Println ("It's before noon" )
default :
fmt .Println ("It's after noon" )
}
// A type `switch` compares types instead of values. You
// can use this to discover the type of an interface
// value. In this example, the variable `t` will have the
// type corresponding to its clause.
whatAmI := func (i interface {}) {
switch t := i .(type ) {
case bool :
fmt .Println ("I'm a bool" )
case int :
fmt .Println ("I'm an int" )
default :
fmt .Printf ("Don't know type %T\n" , t )
}
}
whatAmI (true )
whatAmI (1 )
whatAmI ("hey" )
}
Arrays
Array types are declared as [n]T on Go, where T represents the type and n represents the size of the array. var a [10]int will declare a variable called a that has as type an array of integers with ten values.
The built-in method len(a) — the argument passed will be the array to be checked — will return the array's length.
Example
package main
import "fmt"
func main () {
var a [2 ]string
a [0 ] = "Hello"
a [1 ] = "World"
fmt .Println (a [0 ], a [1 ])
fmt .Println (a )
primes := [6 ]int {2 , 3 , 5 , 7 , 11 , 13 }
fmt .Println (primes )
fmt .Println ("Length of Primes:" , len (primes ))
}
Slices
Slices as an array is a sequence of values, but an array has a fixed number of values, but the Slice is dynamically-sized, and because of this, slices are more common than arrays. Unlike arrays, slices are typed only by the elements they contain (not the number of factors).
The type []T represents a slice of elements with type T.
An array can form a slice by selecting two bounds of the array, like on the following code: a[low: high].
This code will select the low index and exclude the high index. For example, doing a[1, 5] from an array will create a slice from 1 to 4, in other words, an array with three positions.
We can also create an empty slice using
This code will create a slice of int with a capacity of 5 elements.
Instead of just basic operations, slices also support several functions to get richer than the array:
Example
// _Slices_ are a key data type in Go, giving a more
// powerful interface to sequences than arrays.
package main
import "fmt"
func main () {
// Unlike arrays, slices are typed only by the
// elements they contain (not the number of elements).
// To create an empty slice with non-zero length, use
// the builtin `make`. Here we make a slice of
// `string`s of length `3` (initially zero-valued).
s := make ([]string , 3 )
fmt .Println ("emp:" , s )
// We can set and get just like with arrays.
s [0 ] = "a"
s [1 ] = "b"
s [2 ] = "c"
fmt .Println ("set:" , s )
fmt .Println ("get:" , s [2 ])
// `len` returns the length of the slice as expected.
fmt .Println ("len:" , len (s ))
// In addition to these basic operations, slices
// support several more that make them richer than
// arrays. One is the builtin `append`, which
// returns a slice containing one or more new values.
// Note that we need to accept a return value from
// `append` as we may get a new slice value.
s = append (s , "d" )
s = append (s , "e" , "f" )
fmt .Println ("apd:" , s )
// Slices can also be `copy`'d. Here we create an
// empty slice `c` of the same length as `s` and copy
// into `c` from `s`.
c := make ([]string , len (s ))
copy (c , s )
fmt .Println ("cpy:" , c )
// Slices support a "slice" operator with the syntax
// `slice[low:high]`. For example, this gets a slice
// of the elements `s[2]`, `s[3]`, and `s[4]`.
l := s [2 :5 ]
fmt .Println ("sl1:" , l )
// This slices up to (but excluding) `s[5]`.
l = s [:5 ]
fmt .Println ("sl2:" , l )
// And this slices up from (and including) `s[2]`.
l = s [2 :]
fmt .Println ("sl3:" , l )
// We can declare and initialize a variable for slice
// in a single line as well.
t := []string {"g" , "h" , "i" }
fmt .Println ("dcl:" , t )
// Slices can be composed into multi-dimensional data
// structures. The length of the inner slices can
// vary, unlike with multi-dimensional arrays.
twoD := make ([][]int , 3 )
for i := 0 ; i < 3 ; i ++ {
innerLen := i + 1
twoD [i ] = make ([]int , innerLen )
for j := 0 ; j < innerLen ; j ++ {
twoD [i ][j ] = i + j
}
}
fmt .Println ("2d: " , twoD )
}
Slices are like references to arrays.
Examples
package main
import "fmt"
func main () {
names := [4 ]string {
"John" ,
"Paul" ,
"George" ,
"Ringo" ,
}
fmt .Println (names )
a := names [0 :2 ]
b := names [1 :3 ]
fmt .Println (a , b )
b [0 ] = "XXX"
fmt .Println (a , b )
fmt .Println (names )
}
Slice Literals
You can also declare slice literals. It’s like the array declaration but without a pre-determined size.
Slice defaults
Using the default limits, you can omit the high or low bounds of your slice size with slices. The default for the low bound is 0, and the Slice length is the higher bound.
With an array, you will do
With slices, all those expressions are equivalent
Example
package main
import "fmt"
func main () {
hello := []string {"h" , "e" , "l" , "l" , "o" }
world := []string {"w" , "o" , "r" , "l" , "d" }
fullHello := hello [:]
fmt .Println (fullHello )
rld := world [2 :]
fmt .Println (rld )
ell := hello [1 :4 ]
fmt .Println (ell )
wo := world [:2 ]
fmt .Println (wo )
}
Slice length and capacity
Example
package main
import "fmt"
func main () {
s := []int {2 , 3 , 5 , 7 , 11 , 13 }
printSlice (s )
// Slice the slice to give it zero length.
s = s [:0 ]
printSlice (s )
// Extend its length.
s = s [:4 ]
printSlice (s )
// Drop its first two values.
s = s [2 :]
printSlice (s )
}
func printSlice (s []int ) {
fmt .Printf ("len=%d cap=%d %v\n" , len (s ), cap (s ), s )
}
Nil slices
A nil slice hasn’t an underlying array, and its length and capacity are 0.
Examples
package main
import "fmt"
func main () {
var s []int
fmt .Println (s , len (s ), cap (s ))
if s == nil {
fmt .Println ("nil!" )
}
}
Maps
Maps are a built-in associative data type. It’s like Map in Java and is a structure that maps keys and values.
As I said in part 1 of this series, the zero value of a Map is nil.
package main
import "fmt"
func main () {
var m map [string ]int
if m == nil {
fmt .Println ("Map is nil" )
}
// If you try to set a key on this map, the execution will throw
// m["key"] = 1
}
Empty Map
To create an empty map, you can use the make built-in method:
If you get a value from a key that isn’t declared, his value will be zero, related to the type of your map value.
Mutating Maps
You can insert or update an element in the Map
You can pick up the element
Delete the element
With two value assignments, you can also check if a value exists on a map
If the value is in key, the element variable will get the value of this element, and the ok variable will be true.
If the value isn’t in the key, the element variable will be zero (related to the specific type), and the ok variable will be false.
Declaring inline Map
It’s also possible to declare a non-empty Map with filled keys on his creation.
Examples
// _Maps_ are Go's built-in [associative data type](http://en.wikipedia.org/wiki/Associative_array)
// (sometimes called _hashes_ or _dicts_ in other languages).
package main
import "fmt"
func main () {
// To create an empty map, use the builtin `make`:
// `make(map[key-type]val-type)`.
m := make (map [string ]int )
// Set key/value pairs using typical `name[key] = val`
// syntax.
m ["k1" ] = 7
m ["k2" ] = 13
// Printing a map with e.g. `fmt.Println` will show all of
// its key/value pairs.
fmt .Println ("map:" , m )
// Get a value for a key with `name[key]`.
v1 := m ["k1" ]
fmt .Println ("v1: " , v1 )
// The builtin `len` returns the number of key/value
// pairs when called on a map.
fmt .Println ("len:" , len (m ))
// The builtin `delete` removes key/value pairs from
// a map.
delete (m , "k2" )
fmt .Println ("map:" , m )
// The optional second return value when getting a
// value from a map indicates if the key was present
// in the map. This can be used to disambiguate
// between missing keys and keys with zero values
// like `0` or `""`. Here we didn't need the value
// itself, so we ignored it with the _blank identifier_
// `_`.
_ , prs := m ["k2" ]
fmt .Println ("prs:" , prs )
// You can also declare and initialize a new map in
// the same line with this syntax.
n := map [string ]int {"foo" : 1 , "bar" : 2 }
fmt .Println ("map:" , n )
}
Conclusion
I hope that this content has been clear to you. Feel free to reach out with any doubts, and connect on LinkedIn.
See you in the next article! In the next article, we will learn more about how to iterate these built-in structures (map, slices, and arrays and deep down on more Golang features :)
Stay safe :)