Sorting and Manipulating Go Dictionaries

GoGoBeginner
Practice Now

Introduction

Unlike other languages, in Go, dictionaries (maps) are unordered collections. In this lab, we will learn about sorting dictionaries and how to use them more flexibly.

Knowledge Points:

  • Sorting dictionaries
  • Swapping keys and values in a dictionary
  • Slices of dictionaries
  • Dictionaries with slices as values
  • Reference type characteristics of dictionaries

Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL go(("`Go`")) -.-> go/BasicsGroup(["`Basics`"]) go(("`Go`")) -.-> go/FunctionsandControlFlowGroup(["`Functions and Control Flow`"]) go(("`Go`")) -.-> go/DataTypesandStructuresGroup(["`Data Types and Structures`"]) go(("`Go`")) -.-> go/ObjectOrientedProgrammingGroup(["`Object-Oriented Programming`"]) go/BasicsGroup -.-> go/variables("`Variables`") go/FunctionsandControlFlowGroup -.-> go/for("`For`") go/DataTypesandStructuresGroup -.-> go/slices("`Slices`") go/DataTypesandStructuresGroup -.-> go/maps("`Maps`") go/FunctionsandControlFlowGroup -.-> go/range("`Range`") go/FunctionsandControlFlowGroup -.-> go/functions("`Functions`") go/FunctionsandControlFlowGroup -.-> go/closures("`Closures`") go/DataTypesandStructuresGroup -.-> go/structs("`Structs`") go/ObjectOrientedProgrammingGroup -.-> go/struct_embedding("`Struct Embedding`") subgraph Lab Skills go/variables -.-> lab-149095{{"`Sorting and Manipulating Go Dictionaries`"}} go/for -.-> lab-149095{{"`Sorting and Manipulating Go Dictionaries`"}} go/slices -.-> lab-149095{{"`Sorting and Manipulating Go Dictionaries`"}} go/maps -.-> lab-149095{{"`Sorting and Manipulating Go Dictionaries`"}} go/range -.-> lab-149095{{"`Sorting and Manipulating Go Dictionaries`"}} go/functions -.-> lab-149095{{"`Sorting and Manipulating Go Dictionaries`"}} go/closures -.-> lab-149095{{"`Sorting and Manipulating Go Dictionaries`"}} go/structs -.-> lab-149095{{"`Sorting and Manipulating Go Dictionaries`"}} go/struct_embedding -.-> lab-149095{{"`Sorting and Manipulating Go Dictionaries`"}} end

Sorting Dictionaries

Create a map.go file in the ~/project directory:

touch ~/project/map.go

Write the following code into the map.go file:

package main

import (
    "fmt"
)

func main() {
    m := map[string]int{
        "zhangsan": 99,
        "lisi":   38,
        "wangwu":   84,
    }
    for key, value := range m {
        fmt.Println(key, value)
    }
    fmt.Println("\nInsert Data")
    m["cup"]=25
    for key, value := range m {
        fmt.Println(key, value)
    }
}

The output of the program may look like this:

wangwu 84
lisi 38
zhangsan 99

Insert Data
cup 25
wangwu 84
lisi 38
zhangsan 99

The output may vary since the position of inserted data is not fixed.

You can try running the program multiple times and observe that the position of the inserted data may vary.

But sometimes we need to sort the dictionary after inserting data. How can we achieve that?

Since a dictionary itself cannot be sorted, we can convert the dictionary into a slice and then sort the slice.

Sort by Key

First, let's learn how to sort the dictionary by key.

Here are the steps:

  • Convert the keys of the dictionary into slice
  • Sort the slice
  • Get the corresponding value using the dictionary's query method

Write the following code into the map.go file:

package main

import (
    "fmt"
    "sort"
)

func main() {
    // Initialize the dictionary
    m1 := map[int]string{
        3: "lisi",
        1: "zhangsan",
        2: "wangwu",
    }
    keys := make([]int, 0) // Initialize the slice
    for key := range m1 {
        // Convert the key to a slice
        keys = append(keys, key)
    }
    // Sort the key slice using the sort package
    sort.Ints(keys)
    for _, key := range keys {
        // The output is in order now
        fmt.Println(key, m1[key])
    }
}

The output of the program is as follows:

1 zhangsan
2 wangwu
3 lisi

Through this approach, we have achieved sorting the dictionary based on the key.

Swapping Keys and Values in a Dictionary

Before we explain how to sort by value, let's learn how to swap the keys and values in a dictionary.

Swapping keys and values means interchanging the positions of keys and values in a dictionary, as shown in the figure below:

image

The implementation code is simple. Write the following code into the map.go file:

package main

import "fmt"

func main() {
    m := map[string]int{
        "zhangsan": 99,
        "lisi":     38,
        "wangwu":   84,
    }
    m2 := map[int]string{}
    for key, value := range m {
        m2[value] = key
    }
    fmt.Println(m2)
}

The output of the program is as follows:

map[38:lisi 84:wangwu 99:zhangsan]

The essence of this code is to extract the keys and values from the original dictionary, and then reinsert them into the dictionary. It's simple and straightforward.

Sort by Value

Combining the logic of sorting the dictionary by key and swapping keys and values in a dictionary, we can sort the dictionary by value.

Write the following code into the map.go file:

package main

import (
    "fmt"
    "sort"
)

func main() {
    // Initialize the dictionary
    m1 := map[string]int{
        "zhangsan": 99,
        "lisi":     38,
        "wangwu":   84,
    }

    // Initialize the reversed dictionary
    m2 := map[int]string{}
    for key, value := range m1 {
        // Generate the reversed dictionary m2 by swapping the key-value pairs
        m2[value] = key
    }

    values := make([]int, 0) // Initialize the sorting slice
    for _, value := range m1 {
        // Convert the values of the original dictionary to a slice
        values = append(values, value)
    }
    // Sort the value slice using the sort package
    sort.Ints(values)

    for _, value := range values {
        // The output is in order now
        fmt.Println(m2[value], value)
    }
}

The output of the program is as follows:

lisi 38
wangwu 84
zhangsan 99

Now we have sorted the dictionary based on its values.

Sorted by sort.Slice

If the version of Go is later than 1.7, we can use the sort.Slice function to quickly sort a dictionary by key or value.

Here is an example:

package main

import (
    "fmt"
    "sort"
)

func main() {
    m1 := map[int]int{
        21: 99,
        12: 98,
        35: 17,
        24: 36,
    }

    // Define a structure for sorting function values later
    type kv struct {
        Key   int
        Value int
    }

    // Initialize the slice of structures
    var s1 []kv

    // Add the contents of the dictionary to the slice of structures
    for i, j := range m1 {
        s1 = append(s1, kv{i, j})
    }

    // Sort the slice based on key by comparing values
    sort.Slice(s1, func(i, j int) bool {
        // Return true when the key value of the i-th slice is larger than that of the j-th slice
        return s1[i].Key < s1[j].Key
    })

    fmt.Println("Sorted in ascending order by key:")
    for _, j := range s1 {
        fmt.Printf("%d, %d\n", j.Key, j.Value)
    }
}

The output is as follows:

Sorted in ascending order by key:
12, 98
21, 99
24, 36
35, 17

In this program, we used a structure, which we will explain in detail in the later experiments of this chapter.

For now, all you need to know is that a structure is used to store multiple variables of different types. In this program, the kv structure has two integer variables Key and Value.

Then we stored the values of the dictionary in a slice of structures and sorted the slice using the sort.Slice() function and an anonymous function.

The anonymous function Compare() compares the values of the keys in the structure. If the key value of slice i is greater than that of slice j, it will return true. Thus, the slice of structures is sorted in ascending order based on the key.

We can also use sort.Slice to sort the dictionary in descending order based on the key or in ascending order based on the value. Let's do a little test:

Little Test

Create a map2.go file and modify the code from the previous section to sort the map in descending order based on the value.

Expected Output:

Sorted in descending order by value:
21, 99
12, 98
24, 36
35, 17

Requirements:

  • The map2.go file should be placed in the ~/project directory.
  • You must use the sort.Slice function.

Slices of Dictionaries

Just as we can use arrays or slices to store related data, we can also use slices where the elements are dictionaries.

Write the following code into the map.go file:

package main

import "fmt"

func main() {
    // Declare a slice of dictionaries and initialize it using make
    var mapSlice = make([]map[string]string, 3)
    for index, value := range mapSlice {
        fmt.Printf("index:%d value:%v\n", index, value)
    }
    fmt.Println("Initialization")
    // Assign values to the first element of the slice
    mapSlice[0] = make(map[string]string, 10)
    mapSlice[0]["name"] = "louplus"
    mapSlice[0]["password"] = "123456"
    mapSlice[0]["address"] = "Beijing"
    for index, value := range mapSlice {
        fmt.Printf("index:%d value:%v\n", index, value)
    }
}

The code first defines a dictionary type and then initializes a normal slice. Then it adds the dictionary as an element to the slice.

Dictionaries with Slices as Values

We can also use dictionaries with slices as values to store more data in a dictionary.

Write the following code into the map.go file:

package main

import "fmt"

func main() {
    var sliceMap = make(map[string][]string, 3)
    key := "louplus"
    value, ok := sliceMap[key]
    if !ok {
        value = make([]string, 0, 2)
    }
    value = append(value, "Beijing", "Shanghai")
    sliceMap[key] = value
    fmt.Println(sliceMap)
}

The output of the program is as follows:

map[louplus:[Beijing Shanghai]]

The code first declares a slice dictionary type and then initializes a normal dictionary. It then adds a slice as a value to the dictionary.

Reference Type Characteristics of Dictionaries

Arrays are value types, so assigning and passing them to functions changes the original array. In other words, we can modify the array itself by passing it to a function.

Write the following code into the map.go file:

package main

import "fmt"

func modifyMap(x map[string]int) {
    x["lisi"] = 100
}

func main() {
    a := map[string]int{
        "zhangsan": 99,
        "lisi":     38,
        "wangwu":   84,
    }
    // Because the map is passed by reference, the modification in modifyMap changes the original dictionary
    modifyMap(a)
    fmt.Println(a) // map[lisi:100 wangwu:84 zhangsan:99]
}

The output of the program is as follows:

map[lisi:100 wangwu:84 zhangsan:99]

In the example, we demonstrated the reference transfer characteristic of a dictionary, where the reference is passed between functions, and the modifications in the referenced dictionary directly affect the original one.

Summary

In this lab, we have learned about advanced usage of dictionaries in Go, including:

  • Sorting dictionaries by key or value
  • Swapping keys and values in a dictionary
  • Using slices of dictionaries
  • Using dictionaries with slices as values
  • Reference type characteristics of dictionaries

In the next experiment, we will learn about structures in Go.

Other Go Tutorials you may like