Introduction to Golang: Functions, Loops, Maps, Arrays, and Conditionals

Published Sep 01, 2017
Introduction to Golang: Functions, Loops, Maps, Arrays, and Conditionals

Forty-five years since the invention of the C programming language, lots of languages have been developed — some functional, others object-oriented, and some with the spice of both. Golang is a new kid on the block that is becoming more popular by the day.

Why Go

Go is a statically typed language that is easy to learn and forces you to adhere to good programming habits. Although what is termed good programming habits is a bit blurry these days, Go tries to enforce some rules to help remove certain pitfalls we encounter when writing code, e.g., forcing you to remove unused variables or packages.

All You Need to Get Started

Go has a handful of keywords that we'll be going through in this article. By the end of this section, you should be able to:

  1. Understand Packages, variables, and functions
  2. Understand types: arrays, slices, and maps
  3. Control flow: for, if, else, switch

Packages, variables, and functions

For experienced programmers who have worked with languages like Java, C, or C++, it's de facto to have the main function as the entry point of your application. The same goes for Go.

Packages and imports

package main
import (
    "fmt"
)

func main() {
    fmt.Println("Hello world")
}

The above code snippet shows two keywords:

  1. Package: In Go, a package is like a namespace for your application. Every Go file must have a package name and is referenced by the folder where it’s stored. A ToUpper function placed in a string/utility folder will have the package name utility. NB: the file package main seems to be the only package that does not adhere to this rule, meaning the package main can be placed anywhere.

  2. Import: The import keyword is used to import other external methods in Go. The example above is importing the fmt package that we used to print "hello world." In a nutshell, you have just written your first Go program.

You can use Go playground or repl.it to run your “hello world” program.

You can check out Golang’s official site on how to set up Go on your system.

Variables

For those of us coming from the world of JavaScript, good news! Golang uses the var and const keywords to declare variables.

package main

import (
    "fmt"
)

func main() {
    const name string = "enaho Murphy"
    var nationality = "Nigeria"
    age := 67
    fmt.Println(name +"  "+ nationality)
} 

The above snippet shows the use of const and var to declare variables. As in most languages that use const, this tells the compiler that the variable name cannot be reassigned. Unlike the const keyword, variables declared with a var keyword can be reassigned.

Another nitpick is the value at the front of the variables. Those are used to enforce types assigned to variables. If I try to pass an int into the variable name, an error would be thrown.

Below is a list of Golang types:

  1. int8, int. int32, and int64
  2. float32 and float64
  3. string
  4. bytes
  5. rune
  6. uint8, uint32, and uint64

Check out the Golang specs for more types.

One more thing to note is that I did not declare or assign a type to the variable age. Using :=, Golang automatically evaluates the type you're trying to assign and makes the variable that type. If I try assigning a string to age, that will throw an error.

Functions

package main

import (
    "fmt"
)

func main() {
    printName()
    fmt.Println(Multiply(34, 55))
}

func printName() {
    fmt.Println("enaho murphy")
}

func Multiply(a int, b int) int {
    return a * b
}

Functions in Go are declared with the func keyword. As seen in the above snippet, parameters passed into the above function must be type int. If I try passing a string into the multiply function, I'll get an error. Go also forces you to define a type for your return value. If I try returning anything other than an int from the Multiply function, I'll get an error.

One more thing: Go uses caps to mimic encapsulation. Functions in uppercase can be accessed by functions in other packages. All lowercase functions are private to that package.

There is also a function called the init function. If declared, Go first runs code in that function before running the main function.

Note: functions in Golang can return more than one value.

Types: Array, Slices, and Maps

Arrays and Slices

Arrays in Golang are the same as slices. Arrays, however, cannot be resized. A slice does not store data — it just points to an array underneath.

Golang gives you this illusion of resizable arrays by constantly reallocating the array and increasing the length by twice the initial value. With this, you don't have to worry about the size constraint of an array.

package main

import "fmt"

func main() {
    var a [2]string
    a[0] = "Hello"
    a[1] = "World"
    a[3] = "sss"
    
    fmt.Println(a[0], a[1])
    fmt.Println(a)

    primes := [6]int{2, 3, 5, 7, 11, 13}
    fmt.Println(primes)
}

The above snippet shows a declaration of an array of type string, which accepts two values. If you have ever programmed before, assigning “Hello” to the array should be familiar to you.

The code snippet above will throw an out of bound context error because we tried to assign three values into an array that only accepts two. Removing the third assignment will fix that error.

Also, note that arrays in Go use a curly bracket to assign values when the variable is declared. That is a short method of dynamically assigning values to arrays. You'll see me use that syntax a lot in this article.

The next code snippet will show you how to make a slice that can take in varying number of assignments.

package main

import "fmt"

func main() {
    primes := [6]int{2, 3, 5, 7, 11, 13}

    var s []int = primes[1:4]
    s = append(s, 9, 20, 6, 8, 9)
    fmt.Println(s)
}

Slicing our array returns a new array. That returned array is a slice. The slice can be resized. As you can see, I was appending multiple values to s and it worked. You should know, the append will return a new reference, since Go automatically recreates a new array and creates a reference to it. Logging out of the append without assigning the value to s first will return the initial value of s.

Another way to create a slice is to use the make keyword:

package main

import (
    "fmt"
)

func main() {
    s := make([]string, 3)
    fmt.Println("emp:", s)
    s[0] = "a"
    s[1] = "b"
    s[2] = "c"
    fmt.Println("set:", s)
    fmt.Println("get:", s[2])
    fmt.Println("len:", len(s))

    s = append(s, "hello", "world" , "this", "is", "great")
    fmt.Println("Len:", len(s), s) 
}

As you can see, I was able to append more values to initial declaration of s by creating an array with the make keyword. Logging out the value will result in a length of eight, and all the append values will show with the initial values.

You should note that if you try to use the s[3] = "hello" to assign values to a slice that points to an array of 3, it will throw an error. Always remember that slices are pointers to an array.

The append method just creates a new array and returns a pointer to that array. Also, the Len method is used to check the array’s length.

Maps

In a nutshell, a map maps keys to values. Coming from Python, you could call it a dictionary. If you're a Ruby programmer, you could call it a hash. In Golang, we call it a map.

The snippet below shows the two ways we could create maps in Golang.

package main

import (
    "fmt"
)

func main() {
    var newMap map[string]int
    newMap = make(map[string]int)
    newMap["age"] = 15
    newMap["issn"] = 16273844
    
    fmt.Println(newMap)


    var secondMap = map[string]string{
    "name": "Enaho Murphy",
    "age": "55",
    "nationality": "Nigeria",
    }
    fmt.Println(secondMap["name"])
}

We first declared the type of map and then used the make keyword to create a new map assigning it to the newMap variable.

The second method creates and initializes the map, adding new values to it. You could call it a shorthand method of creating maps in Go. You can also see this pattern used when creating an array above.

Control flow: for, if, else, switch

Unlike most languages, Golang only has a for loop. The Golang for loop is very powerful, as it can be used to iterate through an array, mimic a while loop, and even call base on multiple statements.

For

The snippet below will show the for loop in action.

package main

import (
  "fmt"  
)

func main() {
  Print(generateArray(100))
}


func generateArray(num int) []int {
  var array = make([]int, num)
  var count int = 0
  for num > 0 {
    count += 1
    array[count -1] = count
    num -= 1
  }
  return array
}

func secondGenerateArray(num int)  []int {
  var array = make([]int, num)
  for i := 0; i < num; i +=1 {
    array[i] = i + 1
  } 
  return array
}

func Print(arr []int) {
  for key, value := range arr {
    fmt.Println(key, value)
  }
}

From the snippet above, I created three methods:

  1. generateArray takes in an integer and returns a range of numbers from 1 to the passed-in value. The main point is to show you that the for loop in Go can keep executing until a certain condition is met. To create an infinite loop, I just use the forkeyword without any value and use the break keyword to break out of the loop.
i := 10
for {
    fmt.Println("i got caled", i)
    if i <= 5 {
      fmt.Println("Loop terminated")
      break;
    }
    i -= 1
}

NB: You can also use the continue keyword in Golang.

  1. secondGenerateArray: This function uses the for loop to iterate over the number passed in. This is a typical loop found in most languages, like JavaScript and C++, comprised of three parts:
  • init: Sets the initial value of I
  • condition: Sets the condition for the loop to keep iterating. The example states: “if the variable I is less than the number passed in”
  • increment: For every iteration, increment is the value of I
    When I equal the value passed in, the loop terminates.
    I hope you are beginning to see the power of Golang for loops!
  1. The last function shows Golang looping over an array. As you’ve probably already noticed, we are looping over the range of an array that returns the key and value.

    On each iteration, we're just printing out the key and the value. This pattern is also used to iterate over a map.

func main() {
  T :=  map[string]string {
    "name": "Enaho Murphy",
    "age": "44",
  }
  for _, value := range T {
    fmt.Println(value)
  }
}

Notice that I used an underscore for the key. Golang throws an error when unused variables are declared. Since the range will return two values, by using the underscore, I'm telling Go to ignore the key.

If and Else

Control of a program in Go is pretty straightforward. One thing you might need to wrap your head around is the idea of not using brackets when evaluating conditions.If you're a Pythonista, this should be familiar to you.

package main

import (
    "fmt"
    "os"
    "strings"
)

func main() {
    inputs := os.Args
    v := strings.Join(inputs[1:], " ")
    if v != "" {
        fmt.Println("Hello " + v)
    } else {
        fmt.Println("Hello anonymous user")
    }
}

The example above makes use of two Golang packages we have not seen yet. We will use the “os” package to get user inputs.

When that example is ran, any value passed after the file name will be collected in os.Args. This returns an array — the first value is a path to your Golang file and the rest the value you typed after the name of the file.

> go run test.go hello world 

fmt.Println(os.Args) // [filepath, hello, world]

By using the Join method on the strings package, we joined everything, except the file path, by splicing the array returned by os.Args.

I checked whether there was a value in the variable, then printed “hello” with what the user passes in, or, I printed “hello anonymous user.”

Manipulating program flow in Golang is pretty straightforward.

Switch

The switch statement in Golang is as straightforward as the if and else. We can assert a single value and run logical code base on the passed in value.

We will be using the os.Args to get the user inputs and echo different values based on their inputs.

package main

import (
    "fmt"
    "os"
)

func main() {
    inputs := os.Args

    switch inputs[1] {
    case "help":
        fmt.Println("This is all the help you need")
    case "greet":
        fmt.Println("Hello user")
    default:
        fmt.Println("You passed no argument")
    }
}

Pretty straightforward, right?

Wrapping it Up

Putting it all together, let's implement the binary search algorithm, which takes in an array of sorted numbers and returns the position of the value, the count it took to find the value, and the value itself as a map.

Note: I won’t cover in detail what a binary search array is. This article does not cover that, but here is a great resource on tutorials point that covers more about it.

First, let's use our generate function to create a range of arrays.

func GenerateArray(num int) []int {
    var array = make([]int, num)
    for i := 0; i < num; i++ {
        array[i] = i + 1
    }
    return array
}

Now that we can generate sorted arrays, let's implement the sorting algorithm.

func BinarySearch(collection []int, value int) map[string]int {
    low := 0
    high := len(collection) - 1
    result := map[string]int{
        "value":    value,
        "count":    0,
        "position": -1,
    }
    for low <= high {
        mid := (low + high) / 2
        result["count"]++
        if collection[low] == value {
            result["position"] = mid
            break
        } else if collection[high] == value {
            result["position"] = mid
            break
        } else if collection[mid] == value {
            result["position"] = mid
            break
        } else if collection[mid] < value {
            low = mid + 1
        } else {
            high = mid - 1
        }
        
    }

    return result
}

We took the length of the array, added it to the array’s lowest value, and divided it by 2 to get the middle value. We also initiated a map to hold the value, result, and position.

Using a for loop, we're checking whether the low becomes greater than the high — if it does, then the value is not in that array. By checking whether the mid value equals what we are looking for, we can also terminate the loop if it evaluates to true using break.

If the middle value is less than the value we're looking for, we know we need to start searching for the middle value. If the value is less than the mid value, we need to start searching before the mid.

We do this iteratively, until we either find the value, or low becomes either equal to or greater than high. Then, the loop terminates, and our map is returned. This algorithm is also called the divide and conquer algorithm.

Putting it all together will give us something like this:
Screen Shot 2017-08-21 at 1.03.04 PM.png

Summary

This article should cover enough content for you to get started with Golang. We still have much to cover, like interfaces and structs, concurrency, and parallelism, which we’ll discuss in a future article

It has been nice writing this article. Please, if you have issues with the code we have written so far, kindly drop a note in the comment box below.

Discover and read more posts from Enaho Isiwele
get started
Enjoy this post?

Leave a like and comment for Enaho