🐹 Go

المجموعات في Go 1.21: دليل مفصل

Here is the translated HTML article in English:

Sets in Go 1.21

What is a set and why do you need it?

Imagine you have a box where you can only put unique items. If you try to put two identical apples in it, only one will remain in the box. This is a set — a collection where each element occurs only once.

Why is this needed? For example:

  • Check if a product is in the store
  • Remove duplicate ingredients from a recipe
  • Merge guest lists without duplicates

Go 1.21 doesn't have a built-in "set" type, but we can easily create one using map!

How to create a set in Go?

We will use map[string]struct{}. Why struct{}? Because it's an empty struct that takes up no memory — perfect for storing only keys!

// Create an empty set
contacts := make(map[string]struct{})

// Add elements
contacts["Anna"] = struct{}{}
contacts["Boris"] = struct{}{}
contacts["Anna"] = struct{}{} // Duplicate — nothing changes

// Check if an element exists
if _, exists := contacts["Anna"]; exists {
    fmt.Println("Anna is in contacts!")
}

// Delete an element
delete(contacts, "Boris")

// Number of elements
fmt.Println(len(contacts)) // Output: 1

Basic set operations

1. Creating and adding elements

package main

import "fmt"

func main() {
    // Create a set for pizza ingredients
    ingredients := make(map[string]struct{})
    
    // Add ingredients
    ingredients["cheese"] = struct{}{}
    ingredients["tomatoes"] = struct{}{}
    ingredients["cheese"] = struct{}{} // Duplicate won't be added
    
    fmt.Println("Ingredients:", ingredients) // map[cheese:{} tomatoes:{}]
    fmt.Println("Count:", len(ingredients)) // 2
}

2. Checking if an element exists

package main

import "fmt"

func main() {
    products := map[string]struct{}{
        "milk": {},
        "bread":   {},
        "eggs":   {},
    }
    
    var item string
    fmt.Print("Enter product to check: ")
    fmt.Scan(&item)
    
    if _, exists := products[item]; exists {
        fmt.Println("Product is in stock!")
    } else {
        fmt.Println("Product is not in stock.")
    }
}

3. Deleting elements

package main

import "fmt"

func main() {
    shoppingList := map[string]struct{}{
        "milk": {},
        "bread":   {},
        "eggs":   {},
        "cheese":    {},
    }
    
    fmt.Println("Shopping list:", shoppingList)
    
    // Delete an unwanted item
    delete(shoppingList, "milk")
    
    fmt.Println("After deletion:", shoppingList)
}

4. Merging sets

package main

import "fmt"

func main() {
    // Host's shopping list
    hostList := map[string]struct{}{
        "juice":     {},
        "chips":   {},
        "pizza":   {},
    }
    
    // What guests suggested
    guestList := map[string]struct{}{
        "juice":     {},
        "cake":    {},
        "lemonade": {},
    }
    
    // Merge — add everything from guestList to hostList
    for item := range guestList {
        hostList[item] = struct{}{}
    }
    
    fmt.Println("Final shopping list:")
    for item := range hostList {
        fmt.Println("-", item)
    }
}

Typical problem walkthroughs

Problem: Pizza ingredients (unique)

package main

import (
    "fmt"
    "strings"
)

func main() {
    fmt.Print("Enter ingredients separated by commas: ")
    var input string
    fmt.Scan(&input)
    
    // Split the string into parts
    items := strings.Split(input, ",")
    
    // Create a set for unique ingredients
    uniqueIngredients := make(map[string]struct{})
    
    for _, item := range items {
        // Remove extra spaces and add
        cleanItem := strings.TrimSpace(item)
        if cleanItem != "" {
            uniqueIngredients[cleanItem] = struct{}{}
        }
    }
    
    fmt.Println("Unique ingredients:")
    for ingredient := range uniqueIngredients {
        fmt.Println("-", ingredient)
    }
}

Input: cheese, tomatoes, cheese, mushrooms, tomatoes

Output: cheese, tomatoes, mushrooms

Problem: Finding missing products

package main

import "fmt"

func main() {
    // Products that should always be in stock
    required := map[string]struct{}{
        "milk": {},
        "bread":   {},
        "eggs":   {},
        "cheese":    {},
    }
    
    // Ask what is available
    fmt.Println("What products are available? (enter one by one, 'stop' to finish)")
    available := make(map[string]struct{})
    
    for {
        var item string
        fmt.Print("Product: ")
        fmt.Scan(&item)
        
        if item == "stop" {
            break
        }
        available[item] = struct{}{}
    }
    
    // Find what is missing
    fmt.Println("Need to buy:")
    for item := range required {
        if _, exists := available[item]; !exists {
            fmt.Println("-", item)
        }
    }
}

Common beginner mistakes

  • Forgetting to check if an element exists — always use if _, exists := set[key]; exists { ... }
  • Trying to get a value by key — a set has no values, only keys. Use existence check.
  • Confusing map[string]bool and map[string]struct{} — struct{}{} takes 0 bytes, while bool takes 1 byte. For sets, struct{}{} is better.
  • Not trimming spaces — when entering via comma, spaces remain. Always use strings.TrimSpace().

Final example: Contact notebook

package main