Sling Academy
Home/Golang/Creating Bi-Directional Maps in Go for Reversible Lookups

Creating Bi-Directional Maps in Go for Reversible Lookups

Last updated: November 24, 2024

When working with data mappings in programming, a common requirement is to retrieve keys from values and vice versa. In Go, bi-directional maps offer a way to efficiently perform reversible lookups without duplicating storage. This article will guide you through the basics, intermediates, and advanced implementations of bi-directional maps in Go.

Basic Concept of Bi-Directional Maps

A bi-directional map allows you to access the key through the value and vice versa. This mechanism is not built into Go’s maps, so you have to implement it manually by maintaining two separate maps.

Basic Implementation

Let's start with a simple implementation using a struct that holds two maps: a map from key to value and another from value to key.

package main

import (
    "fmt"
)

type BiMap struct {
    kv map[string]int
    vk map[int]string
}

func NewBiMap() *BiMap {
    return &BiMap{
        kv: make(map[string]int),
        vk: make(map[int]string),
    }
}

func (b *BiMap) Add(key string, value int) {
    b.kv[key] = value
    b.vk[value] = key
}

func (b *BiMap) GetByKey(key string) (int, bool) {
    val, ok := b.kv[key]
    return val, ok
}

func (b *BiMap) GetByValue(value int) (string, bool) {
    key, ok := b.vk[value]
    return key, ok
}
}

func main() {
    bimap := NewBiMap()
    bimap.Add("apple", 1)
    bimap.Add("banana", 2)

    value, _ := bimap.GetByKey("apple")
    fmt.Println("Value for 'apple':", value)

    key, _ := bimap.GetByValue(2)
    fmt.Println("Key for value 2:", key)
}

Intermediate Enhancements

Improving this basic implementation can lead us to a more robust system. In the example below, we introduce methods for removing mappings and error handling.

func (b *BiMap) RemoveByKey(key string) {
    if val, ok := b.kv[key]; ok {
        delete(b.kv, key)
        delete(b.vk, val)
    }
}

func (b *BiMap) RemoveByValue(value int) {
    if key, ok := b.vk[value]; ok {
        delete(b.vk, value)
        delete(b.kv, key)
    }
}

These methods ensure that removing a mapping is consistent both ways.

Advanced Implementation Strategies

In an advanced implementation, consider wrapping accesses in read and write locks using Go’s concurrency package to deal with concurrent operations on the bi-directional map.

import (
    "sync"
)

type BiMap struct {
    kv map[string]int
    vk map[int]string
    mux sync.RWMutex
}

func (b *BiMap) Add(key string, value int) {
    b.mux.Lock()
    defer b.mux.Unlock()
    // Add implementation remains the same with locking
}

func (b *BiMap) GetByKey(key string) (int, bool) {
    b.mux.RLock()
    defer b.mux.RUnlock()
    return b.kv[key], true
}

func (b *BiMap) GetByValue(value int) (string, bool) {
    b.mux.RLock()
    defer b.mux.RUnlock()
    return b.vk[value], true
}

The above example uses the sync.RWMutex for allowing locks on reading operations and ensuring data correctness during concurrent writes.

This article offers a comprehensive guide to implementing bi-directional maps in Go. From basics to advanced usages with concurrency, you can adapt these techniques according to your application's needs, ensuring efficient data lookups both ways.

Next Article: Maps as Adjacency Lists: Building Graph Structures in Go

Previous Article: How Go Handles Memory Allocation for Maps Internally

Series: Working with Maps in Go

Golang

Related Articles

You May Also Like

  • How to remove HTML tags in a string in Go
  • How to remove special characters in a string in Go
  • How to remove consecutive whitespace in a string in Go
  • How to count words and characters in a string in Go
  • Relative imports in Go: Tutorial & Examples
  • How to run Python code with Go
  • How to generate slug from title in Go
  • How to create an XML sitemap in Go
  • How to redirect in Go (301, 302, etc)
  • Using Go with MongoDB: CRUD example
  • Auto deploy Go apps with CI/ CD and GitHub Actions
  • Fixing Go error: method redeclared with different receiver type
  • Fixing Go error: copy argument must have slice type
  • Fixing Go error: attempted to use nil slice
  • Fixing Go error: assignment to constant variable
  • Fixing Go error: cannot compare X (type Y) with Z (type W)
  • Fixing Go error: method has pointer receiver, not called with pointer
  • Fixing Go error: assignment mismatch: X variables but Y values
  • Fixing Go error: array index must be non-negative integer constant