03. Arrays, Slices, and Maps

Arindam BaidyaArindam Baidya
12 min read

Array

  • An array is a collection of similar data elements that are stored at contiguous memory locations.

  • Arrays are also known as homogeneous data types since we can store elements of a single datatype in one array and not different datatype in one single array.

Why we need arrays?

In programming, most of the time we need to store large amounts of data of similar type. For example, let’s say we want to store marks secured by students in three subjects. So, instead of creating three different variables to store these values, we can simply create an array called grades and can store these marks together.

  • Arrays are fixed length, once initialize, we cannot change the length of the array.

  • Elements should be of the same data type.

  • Array in golang, has a pointer that points to the first element of the array.

  • Array, also has a property called length, which denotes the number of elements, in the array.

  • Also has a property called capacity, which denotes the number of elements that it can contain.

In case of array, Length and capacity are same.

Array declaration syntax

var <array-name> <[array-size]> <array-datatype>

example. var grades [5] int ; var fruits [3] string

Array declaration

func main() {
    // Declare the arrays
    var grades [5]int
    var fruits [3]string

    // Print the arrays
    fmt.Println("Grades:", grades)
    fmt.Println("Fruits:", fruits)
}

/* Output:
Grades: [0 0 0 0 0]
Fruits: [  ]
*/

Array initialization

  • var grades [5] int = [5]int{10,20,30}

  • grades := [5]int{10,20,30} » Short-hand declaration

  • grades := […]int{10,20,30} » Using ellipses

func main() {
    // Declare and initialize the arrays
    var fruits [2]string = [2]string{"apples", "oranges"}
    marks := [3]int{98, 88, 97}
    names := [...]string{"Rechal", "Monu", "Bini"}

    // Print the arrays
    fmt.Println("Fruits:", fruits)
    fmt.Println("Marks:", marks)
    fmt.Println("Names:", names)
}

/*
Fruits: [apples oranges]
Marks: [98 88 97]
Names: [Rechal Monu Bini]
*/

len()

  • The length of an array refers to the number of elements can store in an array.
func main() {
    // Declare and initialize the array
    var fruits [2]string = [2]string{"apple", "banana"}

    // Print the length of the array
    fmt.Println("Length of fruits array:", len(fruits))
}

// Length of fruits array: 2

Indexes in array

  • An Array is also numbered, and these numbers are called array index.

  • First element of an array is of index 0, and last element is index of array length - 1.

  • Array index lies: 0 <= index <= len - 1

func main() {
    var fruits [5]string = [5]string{"apples", "oranges", "grapes", "mango", "banana"}
    fmt.Println(fruits[2])
}

// grapes
func main() {
    var fruits [5]string = [5]string{"apples", "oranges", "grapes", "mango", "banana"}
    fmt.Println(fruits[6]) // This line will cause an error
}

// invalid array index 6 (out of bounds for 5-element array)

Changing the value of an array

func main() {
    var grades [5]int = [5]int{98, 88, 97, 58, 96}

    fmt.Println(grades[3]) // Prints the initial value at index 3

    grades[3] = 88 // Updates the value at index 3

    fmt.Println(grades[3]) // Prints the updated value at index 3
}

/*
58
88
*/

Looping through an array using for

func main() {
    var grades [5]int = [5]int{98, 88, 97, 58, 96}

    for i := 0; i < len(grades); i++ {
        fmt.Println(grades[i])
    }
}

/*
98
88
97
58
96
*/

Looping through an array using range

  • range keywords is mainly used in for loops in order to iterate over all elements of an array, slices, and maps.

  • For arrays, range sets the scope of iteration up to the length of the array.

  • In case of arrays and slices, from range keywords, first value returned is the index, and second value is the element itself.

func main() {
    var grades [5]int = [5]int{98, 88, 97, 58, 96}

    for index, element := range grades {
        fmt.Println(index, ":", element)
    }
}

/*
0 : 98
1 : 88
2 : 97
3 : 58
4 : 96
*/

Multidimensional arrays

  • More than one dimension

  • Array of arrays

  • An array that has multiple level

  • Simplest multidimensional array is 2D array

func main() {
    arr := [3][2]int{{2, 4}, {5, 7}, {8, 9}}

    fmt.Println(arr[2][1])
}

// Output: 9

Slices

A slice is defined as a continuous segment of an underlying array and provides access to a numbered sequence of elements from that array.

  • Slices provide access to parts of an array in sequential order.

  • They are more flexible and more powerful than an array.

  • As arrays has limitations of being fixed size, slices are variable typed (We can add or remove elements from a slice).

Component of an slice

    1. Pointer, 2. Length, 3. Capacity

Pointer: Pointer is used to point to the first element of the array, that is accible through that slice.

  • Pointers are variable that holds memory address.

Slices have both length and capacity.

  • Length of a slice is the number of elements it contains.

  • The capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice.

  • len() to get length, cap() to get capacity of a slice.

Declaring and initializing a slice

<slice_name> := []<data_type>{<values>} » No need to mention size.

  • grades := []int{89, 90, 85}
func main() {
    grades := []int{89, 90, 85}
    fmt.Println(grades)
}

// [89 90 85]

Slice from an array

array [start_index : end_index]

  • Starting index is included but exding index is excluded.

Example: array[0 : 3]; arr[1 : 6]; arr[ : 4]; arr[ : ]

  • If we don’t mention lower bound/ starting index, slice will start from 0th index by default.

  • If we don’t mention both the index (starting and ending), the entire array will be slices for us.

func main() {
    arr := [10]int{10, 20, 30, 40, 50, 60, 70, 80, 90, 100}
    slice_1 := arr[0:8]
    fmt.Println(slice_1)
}

// [10 20 30 40 50 60 70 80]
func main() {
    arr := [10]int{10, 20, 30, 40, 50, 60, 70, 80, 90, 100}
    slice_1 := arr[1:8]
    fmt.Println(slice_1)

    slice_2 := slice_1[0:3]
    fmt.Println(slice_2)
}

/*
[20 30 40 50 60 70 80]
[20 30 40]
*/

Declaring and initializing a slice using make()

slice := make([]<data_type>, length, capacity)

  • Capacity is optional here.

  • slice : make ([]int, 5, 10)

func main() {
    slice := make([]int, 5, 8)
    fmt.Println(slice)
    fmt.Println(len(slice))
    fmt.Println(cap(slice))
}

/*
[0 0 0 0 0]
5
8
*/
  • Capacity function (cap()) are mostly use for slice, however it can be use for array as well. but the thing is, capacity of an array is equal o the length of that array.
func main() {
    arr := [10]int{10, 20, 30, 40, 50, 60, 70, 80, 90, 100}
    slice := arr[1:8]
    fmt.Println(cap(arr))
    fmt.Println(cap(slice))
}

/*
10
9
*/

As slice is a reference of an array, when we change the value of a slice, that will affect on that array as well.

func main() {
    arr := [5]int{10, 20, 30, 40, 50}
    slice := arr[:3]

    fmt.Println(arr)
    fmt.Println(slice)

    slice[1] = 800

    fmt.Println("After modification")
    fmt.Println(arr)
    fmt.Println(slice)
}

/*
[10 20 30 40 50]
[10 20 30]
After modification
[10 800 30 40 50]
[10 800 30]
*/

Appending to a slice

func append(s []T, vs …T) []T

  • First parameter » s []T » slice of some datatype.

  • Rest are » vs …T » Values of the same datatype.

  • Resulting value of this append function is » []T » a slice containing all the elements of the original slice plus the provided values.

If the underlying array is too small to fit the new values, a bigger array will be allocated. Now this new slice that will be returned will have two times the initial capacity. If the initial capacity could not contain all the elements.

func main() {
    arr := [4]int{10, 20, 30, 40}

    slice := arr[1:3]

    fmt.Println(slice)        // Output: [20 30]
    fmt.Println(len(slice))   // Output: 2
    fmt.Println(cap(slice))   // Output: 3

    slice = append(slice, 900, -90, 50)

    fmt.Println(slice)        // Output: [20 30 900 -90 50]
    fmt.Println(len(slice))   // Output: 5
    fmt.Println(cap(slice))   // Output: 6 (or more, depending on Go's internal allocation strategy)
}

Appending a slice to another slice

slice = append(slice, anotherSlice…)

  • Three dots ... used for variadic functions. That can take an arbitrary number of arguments.
func main() {
    arr := [5]int{10, 20, 30, 40, 50}

    slice := arr[:2] // This creates a slice with elements [10, 20]

    arr_2 := [5]int{5, 15, 25, 35, 45}

    slice_2 := arr_2[:2] // This creates a slice with elements [5, 15]

    new_slice := append(slice, slice_2...) // Appends elements of slice_2 to slice

    fmt.Println(new_slice) // Output: [10 20 5 15]
}

Deleting from a slice

func main() {
    arr := [5]int{10, 20, 30, 40, 50}

    i := 2

    fmt.Println(arr) // Output: [10 20 30 40 50]

    slice := arr[:i] // This creates a slice with elements [10, 20]

    slice_2 := arr[i+1:] // This creates a slice with elements [40, 50]

    new_slice := append(slice, slice_2...) // Appends elements of slice_2 to slice

    fmt.Println(new_slice) // Output: [10 20 40 50]
}

Copying one slice to another

func copy(dest, src []Type) int

  • copying into a destination slice from a source slice.

  • It also returns, the number of element that has been copied, which is the minimum of the length of the destination slice or the length of the source slice.

num := copy(dest_slice, src_slice)

  • Both slice need to be of same datatype.
func main() {
    src_slice := []int{10, 20, 30, 40, 50}

    dest_slice := make([]int, 3)

    num := copy(dest_slice, src_slice)

    fmt.Println(dest_slice) // Output: [10 20 30]

    fmt.Println("Number of elements copied: ", num) // Output: Number of elements copied:  3
}

Looping through a slice using range

func main() {
    arr := []int{10, 20, 30, 40, 50}

    for index, value := range arr {
        fmt.Println(index, ":→", value)
    }
}

/*
0 :→ 10
1 :→ 20
2 :→ 30
3 :→ 40
4 :→ 50
*/

Replacing the first return value using underscore (_)

func main() {
    arr := []int{10, 20, 30, 40, 50}

    for _, value := range arr {
        fmt.Println(value)
    }
}

/*
10
20
30
40
50
*/

Maps

  • A map is a data structure that provides you with an unordered collection of key-value pairs.

  • They are also sometimes called associative arrays in PHP, or hash tables in Java, or dictionaries in Python.

  • They are used to look up a value by its associated key. We store values into map based on a key.

  • The strength of a map is its ability to retrieve data quickly based on the key.

  • A key works like an index that points to the value we associate with that key.

  • Go maps are implemented by hash tables, and they have efficient get, add, and delete operations.

Declaring and initializing a map

var <map_name> map[<key_data_type>]<value_data_type>

var my_map map[string]int » This index creates a nil map. In maps, the zero value of the map is nil, and a nil map does not contain any key.

  • If we try to add a key-value pair to a nil-map, the compiler will throw us a runtime error.

Creating a map

func main() {
    var codes map[string]string

    codes["en"] = "english"

    fmt.Println(codes)
}

// panic: assignment to entry in nil map

To create maps with key-value pairs, we need to initialize them as well.

<map_name> := map[<key_data_type>]<value_data_type>{<key-value-pairs>}

codes :map[string]string{“en“: “english“, “fr”: “french”}

Creating and initializing a map

func main() {
    // Declare and initialize the map with key-value pairs
    codes := map[string]string{
        "en": "english",
        "fr": "french",
    }

    // Print the map
    fmt.Println(codes)
}

//Output: map[en:english fr:french]

Declaring, and initializing a map using make() function

<map_name> := make(map[<key_data_type>]<value_data_type>, <initial_capacity>)

  • Capacity over here is an optional argument
func main() {
    // Initialize an empty map
    codes := make(map[string]int)

    // Print the map
    fmt.Println(codes)
}

// map[] >> Empty map

Length of a map len()

  • To determine how many items or key-value pairs a map has.
func main() {
    // Initialize the map with key-value pairs
    codes := map[string]string{"en": "english", "fr": "french", "hi": "hindi"}

    // Print the length of the map
    fmt.Println(len(codes))
}

// Output: 3

Accessing a map

func main() {
    // Initialize the map with key-value pairs
    codes := map[string]string{
        "en": "english",
        "fr": "french",
        "hi": "hindi",
    }

    // Print the values associated with each key
    fmt.Println(codes["en"])
    fmt.Println(codes["fr"])
    fmt.Println(codes["hi"])
}

/*
english
french
hindi
*/

Getting a key

Getting a key in maps » getting the value associated with that key

value, found := map_name[key]

  • value » is the value of that key

  • found » it is optional, is a boolean, indicates if the key exist or not.

func main() {
    // Initialize the map with key-value pairs
    codes := map[string]int{
        "en": 1,
        "fr": 2,
        "hi": 3,
    }

    // Check for the presence of the key "en"
    value, found := codes["en"]
    fmt.Println(found, value)

    // Check for the presence of the key "hh"
    value, found = codes["hh"]
    fmt.Println(found, value)
}

/*
true 1
false 0
*/

Adding key-value pairs

func main() {
    // Initialize the map with key-value pairs
    codes := map[string]string{"en": "english", "fr": "french", "hi": "hindi"}

    // Add a new key-value pair
    codes["it"] = "italian"

    // Print the map
    fmt.Println(codes)
}

// Output: map[en:english fr:french hi:hindi it:italian]

Update key-value pairs

Overriding the existing value

func main() {
    // Initialize the map with key-value pairs
    codes := map[string]string{"en": "english", "fr": "french", "hi": "hindi"}

    // Update the value for the key "en"
    codes["en"] = "english langauge"

    // Print the map
    fmt.Println(codes)
}

// Output: map[en:english language fr:french hi:hindi]

Delete key-value pairs

  • delete(map, key_name) » Takes Map name, and key_name as input
func main() {
    // Initialize the map with key-value pairs
    codes := map[string]string{"en": "english", "fr": "french", "hi": "hindi"}

    // Print the map before deletion
    fmt.Println(codes)

    // Delete the key "en"
    delete(codes, "en")

    // Print the map after deletion
    fmt.Println(codes)
}

/*
map[en:english fr:french hi:hindi]
map[fr:french hi:hindi]
*/

Iterate over a map using range

  • In array range returns index and value

  • In slice range returns key and value

func main() {
    // Initialize the map with key-value pairs
    codes := map[string]string{"en": "english", "fr": "french", "hi": "hindi"}

    // Iterate over the map and print each key-value pair
    for key, value := range codes {
        fmt.Println(key, "->", value)
    }
}

/*
en -> english
fr -> french
hi -> hindi
*/

Truncate a map

  • Deleting/clearing all elements
  1. Iterating over the map and deleting one by one

func main() {
    // Initialize the map with key-value pairs
    codes := map[string]string{"en": "english", "fr": "french", "hi": "hindi"}

    // Iterate over the map and delete each key-value pair
    for key := range codes {
        delete(codes, key)
    }

    // Print the map after deletion
    fmt.Println(codes)
}

// Output: map[]
  1. Reinitializing the map with an empty map

func main() {
    // Initialize the map with key-value pairs
    codes := map[string]string{
        "en": "english",
        "fr": "french",
        "hi": "hindi",
    }

    // Reinitialize the map to an empty map
    codes = make(map[string]string)

    // Print the map after reinitialization
    fmt.Println(codes)
}

// Output: map[]

References

KodeKloud

0
Subscribe to my newsletter

Read articles from Arindam Baidya directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Arindam Baidya
Arindam Baidya