HomeArchitectureIt is time to Go: our journey into grown-up code part 7

It is time to Go: our journey into grown-up code part 7

This is the seventh article in our series in learning GO, the first four articles can be found here:

  1. It is time to “Go” and learn how to code with Go
  2. It is time to Go: our journey into grown-up code part 2
  3. It is time to Go: our journey into grown-up code part 3
  4. It is time to Go: our journey into grown-up code part 4
  5. It is time to Go: our journey into grown-up code part 5
  6. It is time to Go: our journey into grown-up code part 6

To date, our journey has covered a lot of ground, and we have slowly built up an understanding of several core features and concepts.  We are truly building firm foundations.

  • The core Variables types
  • Variadic variables
  • Redeclaration
  • Constants
  • inputs
  • Structs
  • Pointers
  • Interfaces
  • Function basics
  • Conditional statements, if statements and switch statements.
  • Loops, breaks, deferment, continue, goto, return and recover
  • Range
  • Importation of built-in functions (“fmt”, “sync”, “reflect”, “strings”)

At the end of our last article, we extended our understanding of loop code with the addition of “nested” loops and the introduction of “break”, “continue” and the rarely used “goto” which is a pointer that uses a “label:” to control code flow.

What are we looking at today?

Today we are looping back and continuing our journey into Arrays and Splits.  We have already touched on Arrays and Splits albeit at a very high level. In an earlier article  Today we will once again climb into our scuba gear and start to break these down with a deeper dive.  As normal we will start with our definitions and move onto some working examples of Arrays, Slices and their manipulation.  Arrays and Splits like Loops are a major building block of the Go language, as they store the entities that need to be manipulated to get the object of the Go program carried out.

What is an Array in Go?

Arrays are a collection of data structures that have a predetermined number of elements. This means that an array’s size is fixed, a benefit of this is that the data structure only has to be allocated memory once. This is in contrast with splits that we will discuss later, where the data structure has variable length and will need to allocate memory dynamically so that it can grow or shrink in the future. Although working with arrays can appear somewhat stiff due to their set length, any program will run faster and perform better thanks to the one-time memory allocation. therefore, developers will frequently employ an array when optimizing algorithms for data structures that never require a changeable number of components.

There are some rules regarding arrays, these are that they are group of objects that have the same data type. They can be made up of strings or numbers. Further, it is not allowed to mix different data types within an array in Go because it is a statically typed language.


NOTE:  I think that statement “Statically typed Language” needs some definition, there are two types of checking static and dynamic.  With a statically typed language Like Go, C++, all type checking occurs at the compile time, compiling turns the code from human-readable to machine-readable, and the resultant programs are often termed binaries.  Languages like JavaScript or Python utilise Dynamic Type Checking, here the verification is done at Runtime, and the written code is not complied into a binary file.


OK let’s get back on track again.  An array in Go once defined always has the same size. The array’s size cannot be changed once it has been defined with a specific size; it can only be increased or decreased.  What! That makes no sense, if something can only be defined with a specific size how can it be increased or decreased, this is where Slices come in, we will discuss these in the next section.

Finally, an array is regarded as a composite or abstract data type because it is composed of primitive or concrete data types like int, string, and bool, among others. In other words, an array is something that is made up of multiple items of a single data type.

How to declare an array in Go

The act of declaring an array is as simple as creating a special variable the format for this creation is shown below.

Array name [size of array] type of data {data values - optional}

So now that we understand the format, let’s look a real array declaration.

A[5]int

Above we have created an array of ten integers.  This means that this object will hold exactly 10 elements. Now because we have not assigned any values, currently the array will effectively be read as “{0. 0. 0. 0. 0. 0. 0. 0. 0. 0}”.

Let us have a look at some other options for creating declaring arrays.

a[5] = 100

Here we have created an array of four objects, one of which will be 100. This will return as “{0, 0, 0, 0, 100}”. In our next example we declare and initialize is a single line.

a := [5]int{20, 40, 60, 80, 100}

Normally an array is a single dimension,  you can create multi-dimensional arrays; the snippet below show how to do this

var twoD [2][3]int

OK let look at this in action, I wrote this piece of code to display our declarations.

package main
import ". fmt"
func main() {
    var a [5]int
    Println("empty:        ", a)
    a[4] = 100
    Println("set:          ", a)
    Println("get:          ", a[4])
    Println("len:          ", len(a))
    b := [5]int{1, 2, 3, 4, 5}
    Println("declared:     ", b)
    var twoD [2][3]int
    for i := 0; i < 2; i++ {
       for j := 0; j < 3; j++ {
          twoD[i][j] = i + j
       }
    }
    Println("2 dimensions: ", twoD)
}

Running this code results in the following response, exactly what we expected.

Arrays, many ways to view them in Go
well there was a lot of infomation there.

I do not think that we need to delve further into this code as there is nothing unexpected.

OK lets now create an array which is made out of a string of words. To save time we will also initialise it as a variable and add the contents

PremierLeague := [20]string{“Arsenal”,” Manchester City”, “Newcastle United”, “Manchester United”, “Tottenham Hotspurs”, “Aston Villa”, Brighton /& Hove Albion”, “Liverpool”, “Brentford”, “Fulham”, “Chelsea”, “Crystal Palace”, “Wolverhampton Wanderers”, “West Ham United”, “Bournemouth”, “Leeds United”, “Everton”, “Nottingham Forrest”, “Leicester City”, “Southampton”}

OK let’s look at some code to display this.

package main
import (
    . "fmt"
)
func main() {
    PremierLeague := [20]string{"Arsenal", "Manchester City", "Newcastle United", "Manchester United", "Tottenham Hotspurs", "Aston Villa", "Brighton & Hove Albion", "Liverpool", "Brentford", "Fulham", "Chelsea", "Crystal Palace", "Wolverhampton Wanderers", "West Ham United", "Bournemouth", "Leeds United", "Everton", "Nottingham Forrest", "Leicester City", "Southampton"}
    Println("Number of Teams in League:          ", len(PremierLeague))
    for _, element := range PremierLeague {
        Println("Names of the Teams in the League:   ",element)
    }
}

What! I thought that we had finished with loops, and now we were concentrating on looking at arrays?  Well, remember that a loop, well it loops. Here, the “for” statement loops though the elements in the array and prints them to standard out put one line at a time.  The interesting part of this is the use of the special character “_”, this is the first time that we have seen this little beasty.

    for _, element := range PremierLeague {

The “_” character is used as a placeholder for the index of each element, which is not used in this loop, and element is a variable that holds the value of each element in the slice during each iteration of the loop.  In other words of for statement reads “for each element in the range of our array PremierLeague, run the following code”. So how do that look in action?  See below.

Wow, an array of the Premier League in Go
This is the Premier League in a pretty array.

Can we use maps in an array?

The simple answer is, yes, we can.  First a reminder of what a map is in GoLang.  It  is a data type that contains a key of a certain type and a value of the same type. Maps are a very efficient way to store and retrieve data that needs unique identifiers.  A very simple map would look like:

var map_name map[key_type]value_type

So to declare a map type we would say

var countries map[string]string.

we could also say

countries := map[string]string

But let’s introduce a new concept the “make()” function; “make()” is a built-in function that is used to allocate and initialise complex data structures, such as a map. When would you use this? The more appropriate time would be If you want to create a map but not add values, ie. Intitalise the map with your capacity. In our example below, we can see our initialisation line.

    m := make(map[string]string,4)

Here we have created the make() function to create a map with a key entity of string, and a value entity of string, and a capacity of 4 entities.

    m[“Firstname”] = “Tom”
    m[“Lastname”] = “Howarth”
    m[“Gender”] = “Male”
    m[“Status”] = “Married”

our next four lines actually add the data to the map.  So how is this functionally different from using

“m := map[string]string{“Firstname”: “Tom”, “Lastname”: “Howarth”, “Gender”: “Male”, “Status”: Married”} “. The simple answer is it is not.

We then convert the map into an array.

    arr := []string{}
    for k, v := range m {
        arr = append(arr, k, v)
    }

As you can see we have initialised a string array, then using our trusty for loop, passed the map to the array with two variables “k (key)” and “v (value)” using “range” of our “map (m)” as the input.  Finally we print out the resultant array.

package main
import (
   .  "fmt"
)
func main() {
    m := make(map[string]string)
    m[“Firstname”] = “Tom”
    m[“Lastname”] = “Howarth”
    m[“Gender”] = “Male”
    m[“Status”] = “Married”
    m[“
    arr := []string{}
    for k, v := range m {
        arr = append(arr, k, v)
    }
    Println(arr)
}

Running the code, results in the following response.

go run map2.go
OH look we converted a map to an array with Go

So as we have shown, we can pass a map to an array, but as an array is a fixed size and cannot be grown or shrunk once initialised is this actually a good thing?  Another thing about maps is that they can contain data of differing types, for example a map can be initialised as shown below.

    m := make(map[string]int)

OK but doesn’t this mean that maps with disparate data types cannot be converted into an arrays?  That is correct; you cannot directly pass a map with disparate data formats directly into an array.  Any attempt to do this will error out.  I wrote the following multi-type map and attempted to convert it to an array.

package main
import (
   .  "fmt"
)
func main() {
    m := make(map[int]string,4)
    m[1] = "Tom"
    m[2] = "Howarth"
    m[3] = "Male"
    m[4] = "Married"

    arr := []string{}
    for k, v := range m {
        arr = append(arr, k, v)
    }
    Println(arr)
}

When I run the code, it resulted in the following error:

Convert a mixed type map to an array in go - fai;
opps that doesn’t look right.

This was not unexpected, and the resultant error is actually very informative and points exactly to the reason of the error.  So that is the end of the journey then?  Whoa there Cowboy.  I never said that it could not be done, just that it is not as straight forward.  What is needed is that one of the data types will need to be converted. Either a string will need to become an integer or an integer will need to become a string; most likely the conversion will be to change the integer to a string as a string just contains characters, whereas an integer must be a valid number.

OK so how do we do that?

My first pass was on attempting to undertake the data transformation, was to pass the values of the key value through another make command, that creates the array and them appending the resultant values to the new array.

package main
import (
   .  "fmt"
)
func main() {
    m := make(map[int]string)
    m[0] = "a"
    m[1] = "b"
    m[2] = "c"
    m[3] = "d"
    k := make([]string, 0, len(m))
    for _, value := range m {
        k = append(k, value)
    }
    Println(k)
}

Running this code results in the following response.

Not quite what we expected to get in go
Opps, that is not quite what we wanted.

This result was not quite what we wanted, as the code striped out the integers keys and only passed the values of the strings to the resultant array.

We therefore needed something else; after a bit of head-scratching I found it was time for a new built-in function import. Enter stage left and take a bow, the built-in function “strconv”, can you guess what it does?  Yep, this built-in function converts Integers to Strings (“Itoa”) and Strings to Integers (“Atoi”).  There are other uses but talking about those now would just muddy an already cloudy pond.

OK so what should we do?

The code below introduces the concept of data conversion, by importing the “strconv” function into our package we gain the ability to convert the integers that make up the keys of our map into strings to enable them to be passed into an array.

package main
import (
    . "fmt"
    "strconv"
)

func main(){
m := make(map[int]string)
   m[1] = "Tom"
   m[2] = "Howarth"
   m[3] = "Male"
   m[4] = "Married"

//Code to convert integers to strings and create a single array from the keys and values
   myArray := make([]string, 0, len(m)*2)
   for k, v := range m {
      myArray = append(myArray, strconv.Itoa(k), v)
   }
   Println(myArray)
}

Running the code results in the following response, which is much more like we expect.

Eureaka - success in Go
Now that looks more like what we wanted.

The first line of interest is the initialisation of our array

myArray := make([]string, 0, len(m)*2)

you will have seen this before, but not that we have *2 at the end of this line, this means the code will assign a block of memory twice the size of our “map m”.  This prevents the need for the code to having to revisit memory whilst appending our elements to the array.  The other elements just for a refreshers state that the array is containing strings and in initialised empty or with a value of “0”.

for k, v := range m {
                        myArray = append(myArray, strconv.Itoa(k), v)
}

Our little for loop, takes both the Keys “k”, and the Values “v” from the map, using the “range” command to capture them.  Our action lines simply append the values of the keys and values to the string, however the thing to note here is the command “strconv.Itoa(k)”, which converts the inputted integer as an outputted string value.  This is our magic sauce.  Finally we print out the resultant array to standard output.

Summary

I know we were going to discuss slices in this article, but as we have already passed 2500 words it is probably better to close the curtain on this particular act.  In our next article we will discuss the Golang construct of the slice.  Delving deeper into the manipulation of ones and zeros that is necessary for proper code writing.

NEWSLETTER

Receive our top stories directly in your inbox!

Sign up for our Newsletters

spot_img
spot_img

LET'S CONNECT