Understanding Data Structures in Golang

Image for post
Image for post

Golang has gained tremendous grounds in Software development in few years of its launch.

I don’t intend to give you the full history of the language, but go the business of the topic.

The basic data structures of Golang include:

  1. Array
  2. Slice
  3. Map
  4. Struct

1. Array

This data structure is used to store fixed number of elements. So once an array is defined, elements cannot be added or removed from the array. Note that, we can set the values for any of the index in the array.

Since Go is all about types, we define the array with the type of element it stores:

There are two ways we can define an array:

a. Defining the array to have package scope. Package scope it can be used different files that have a particular package name.

var myArray [size]type  //general syntax

Examples:

var integerArray [5]int  //An integer array with five elements
var stringArray [4]string //A string array with four elements

Run this code in

b. Defining the array inside either the main function or any other function in your code. Such array is only accessible in the function that it is defined.

a := [5]int{10,20,30,40,50} //Defining and populating an array
b := [4]string{"first", "second", "third", "fourth"}

The “:=” is a short declaration operator

Run this code in

Since array is a sort of “limited” in certain ways, it is advisable to use slice as discussed below:

2. Slice

Slices give a more robust interface to sequences compared to arrays. Unlike an array, no need to specify the length of the slice when defining it.

We can create a slice in three way:

  • Using var: var s []int
  • Using Shorthand Notation: s := []int{1,2,3,4,5}
  • Using make( ): s := make([]int, n) n is the number of the elements

a. Package scope slices:

var slice_name [ ]type  //Syntax 

Observe that we didn’t define “size”

Examples:

Run this code in

Output:

This is the integer Slice:  [10 20 30 40]
This is the string slice: [first second third]
Print the integers: [10 20 30 40]

b. Function scope slice:

Here slices are defined inside functions:

c. Slice Manipulations

We would explore how we can perform a kind of “crud” operation with slice

From the above slice example:

s = []int{10,20,30,40}

i. Add to a slice:

We can add elements to this slice using “append” builtin function:

s = append(s, 50)so, s = []int{10,20,30,40,50}

We can append more than one element to the slice:

s = append(s, 60, 70)so, s = []int{10,20,30,40,50,60,70}

ii. Delete from a slice:

If “a” is a slice and we want to remove element at the “i” index, we do:

a = append(a[:i], a[i+1]...) //... is a variadic argument in Go

Say we want to remove “30” which is the third element at index “2”

s = append(s[:2], s[2+1:]...) so, s = []int{10,20,40,50,60,70}

iii. Replace an element with another

If “a” is a slice and we want to replace element at the “i” index, with the last element:

a[i] = a[len(a)-1]

Say i want replace the third element now “40” (index of 2) with the last element “70”:

s[2] = s[len(s)-1]
so, s =[]int{10,20,70,50,60,70}

iv. Get particular elements from the slice

If “a” is a slice and we want to get particular elements from the slice.

a = []int{i, j, k, l, m, n}

To get elements j to m , we do:

a = a[j:n] //j is included, but n is not

We now have the slice: s =[]int{10,20,70,50,60,70}

To get the 2nd(index 1) to the 4th(index 3) element, we do:

s = s[1:4]
so, s =[]int{20,70,50}

v. Length of Slice:

Using: len(s)

The “crud” code:

Output:

Slice created with 'make':  [10 20 30 40]
Added one element to slice: [10 20 30 40 50]
Added two elements to slice: [10 20 30 40 50 60 70]
Deleted one element from slice: [10 20 40 50 60 70]
Slice with element replaced: [10 20 60 50 60 70]
Updated Slice with element replaced: [10 20 70 50 60 70]
Slice with second to fourth element: [20 70 50]
Length: 3
Capacity: 7
This is the new slice: [20 70 50]

Run this code in

d. Loop through a Slice

We can do this in two ways:

  1. Using range
  2. Using for loop

Run the code in

e. Nested Slice:

A slice can be nested:

nestedInt := make([][]int, 0) //slice of a slice of int
nestedString := [][]string{slice1, slice2}//slice of slice of string

Output:

[[0 1 2 3] [0 1 2 3] [0 1 2 3]]Record:  0
Laptop name: MacbookPro
Laptop name: MacbookAir
Record: 1
Laptop name: hp650
Laptop name: hpEliteBook

Run the code in

3. Maps

It is possible for you to use key value pairs to refer to an element. This is accomplish using map. If you coming from a javascript, php background, see map as your object .

General syntax:

map[keyType]valueType

Map can be defined in three ways:

i. Using var:

var sampleMap = map[string]int

The above is the general syntax that applies both in package and function scope the key is of type string, while the value of the map is of type int.

ii. Using Shorthand Notation:

sampleMap := map[string]int

iii. Using make( ):

sampleMap := make(map[string]int)

a. Defining a simple map

Output:

Print the age of James:  50
James is 50 years old
Ali is 39 years old

Run code here:

b. Just like Slice, we can Add, Remove, Replace elements in a Map:

Output:

Currency with USD added:  map[AUD:Australia Dollar CHF:Switzerland Franc GBP:Great Britain Pound JPY:Japan Yen USD:USA Dollar]Currency with GBP deleted:  map[AUD:Australia Dollar CHF:Switzerland Franc JPY:Japan Yen USD:USA Dollar]Currency with AUD value replaced with NZD:  map[AUD:New Zealand Dollar CHF:Switzerland Franc JPY:Japan Yen USD:USA Dollar]JPY might be equal to: Japan Yen
CHF might be equal to: Switzerland Franc
USD might be equal to: USA Dollar
AUD might be equal to: New Zealand Dollar

Run the code here:

c. Comma Ok Idiom:

From the above example , we can check if a key exist, if they do, we the found value and “ ok” to it :

if value, ok := currency["USD"]; ok { //comma ok idiom
fmt.Printf("The value %s is present\n", value)
fmt.Println(ok) //This will print "true"
} else {
fmt.Println("We could not find that key")
}

d. Map with a key of string/int and value of a slice of string/int:

Example: A map with a key of string and value of a slice of string:

map[string][]string

Run code here:

Output:

Nnamdi likes 
0 Sleeping
1 Watching Movie
2 Eating
Timi likes
0 Watching Cartoon
1 Dreaming
2 Laughing
3 Lazing around

e. Nested Map: Map inside a Map

Run the code here:

Output:

Currency Name: Great Britain Pound
Currency Code: GBP
Ranking: 1

Currency Name: Euro
Currency Code: EUR
Ranking: 2

Currency Name: USA Dollar
Currency Code: USD
Ranking: 3

4. Struct

Go struct is a collection of named fields/properties. A struct can have the same or different types of fields

A struct is defined using the “type” keyword:

type person struct {
firstName string
lastName string
age int
}

Above, we defined a person struct that have three fields with their datatypes.

a. A simple Struct

Run the code here:

Output:

The is the person:  {Mark Kedu 30}

b. Struct with a slice field:

type animal struct {
name string
characteristics []string
}

We use dot(.) to accees the individual elements in a struct:

Run the code here:

Output:

Lion
Eats human
Wild Animal
King of the jungle

c. Nested Struct:

A struct can be used inside another struct as seen below:

Run code here:

Output:

Animal name: Goat
Eats human? false
Characteristics:
Lacks sense
Lazy
Eats grass

d. Promotion:

From the example above, we can call the fields in the animal struct directly like this:

fmt.Println("Animal name:", herb.name)fmt.Println("Eats human? ", herb.eatHuman)fmt.Println("Characteristics:")for _, v := range herb.characteristics {fmt.Printf("\t %v\n", v)}

Run code here:

Observe that we don’t need animal to access its fields again. It means that the animal fields are now seen as herbivores fields. This is called promotion.

e. Anonymous Struct:

A struct can be defined together with its field values. The example below have a struct with map and slice fields

Run code here:

Output:

Steven
Abdul 34789657
Kally 28993332
Tim 12345678
0 Pepsi
1 Cocacola

f. Functions with Receivers (Methods)

A function can be defined that have a receiver of type struct:

If we define a struct:

type animal struct {
name string
characteristics []string
}

We can define a function that “receives” this struct. Lets say the function tells us if an animal can run

func (a animal) run(){
...
}

Run the code here:

Output:

Elephant is a lazy animal hence cannot run

From the above example, you can liken the run() function to be a field of the animal struct, because it is called just the same way a field is called (using the dot notation).

Let us cement the understanding we have about “receiver” by looking at interfaces.

g. Interfaces

Interfaces are named collections of method signatures. An interface is used to achieve polymorphism in Go.

What do i mean? An example perhaps will clarify what is said.

General Syntax:

type interfaceName interface {
...
}

Run the code here:

Output:

This is the area of the circle:  19.634954084936208
This is the area of the square: 2.25

From the example above, we can see polymorphism happening. That is, the area() function is used for calculating the area of the circle and the square. We means the interface is implemented in the square and circle types because they both define the interface method: area()

So, that was a brief explanation of the data structures in Golang. For further reading, check the official documentation:

Also these:

Thank you for taking time to read. You can drop comments if any.

Written by

Software Engineer/Gopher,

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store