Chuyển tới nội dung chính

Map trong Go - Cách lưu trữ dữ liệu theo cặp! 📚

Chào mừng bạn đến với bài học về map trong Go! Giống như một cuốn từ điển, map giúp chúng ta lưu trữ thông tin theo cặp chìa khóa-giá trị. Trong bài học này, chúng ta sẽ tìm hiểu cách sử dụng map một cách hiệu quả.

Map là gì? 🤔

Giống như một cuốn từ điển:

  • Có từ (key)
  • Có nghĩa (value)
  • Dễ dàng tra cứu

1. Khai báo map (Tạo từ điển mới) 📝

// Tạo từ điển rỗng
var scores map[string]int

// Tạo từ điển và thêm từ ngay
scores := map[string]int{
"Alice": 90,
"Bob": 85,
"Charlie": 95,
}

💡 Giải thích:

  • map[string]int: Từ điển có từ là chữ, nghĩa là số
  • {}: Thêm các cặp từ-nghĩa ngay khi tạo

2. Tạo map với make (Tạo từ điển có kế hoạch) 🏗️

// Tạo từ điển rỗng
scores := make(map[string]int)

// Tạo từ điển có sẵn chỗ cho 10 từ
scores := make(map[string]int, 10)

💡 Giải thích:

  • make: Tạo từ điển mới
  • Số 10: Dự kiến sẽ có 10 từ

Thao tác với Map 🎯

1. Thêm từ mới (Thêm cặp key-value)

scores["David"] = 88

💡 Giải thích:

  • "David": Từ mới
  • 88: Nghĩa của từ

2. Tra từ (Truy cập giá trị)

// Cách 1: Tra đơn giản
score := scores["Alice"]

// Cách 2: Tra an toàn
score, exists := scores["Alice"]
if exists {
fmt.Printf("Điểm: %d\n", score)
}

💡 Giải thích:

  • Cách 1: Nhanh nhưng không biết từ có tồn tại không
  • Cách 2: An toàn hơn, biết được từ có tồn tại

3. Xóa từ (Xóa cặp key-value)

delete(scores, "Bob")

💡 Giải thích:

  • delete: Xóa một từ và nghĩa của nó

4. Duyệt từ điển (Xem tất cả từ)

// Cách 1: Xem cả từ và nghĩa
for name, score := range scores {
fmt.Printf("%s: %d\n", name, score)
}

// Cách 2: Chỉ xem từ
for name := range scores {
fmt.Println(name)
}

💡 Giải thích:

  • range: Duyệt qua từng cặp từ-nghĩa
  • Có thể bỏ qua nghĩa nếu không cần

Map với các kiểu dữ liệu khác nhau 🎨

1. Map với struct (Từ điển có nghĩa phức tạp)

type Person struct {
Name string
Age int
Email string
}

people := map[string]Person{
"john": Person{
Name: "John Doe",
Age: 30,
Email: "[email protected]",
},
}

💡 Giải thích:

  • Nghĩa của từ là một hồ sơ
  • Chứa nhiều thông tin khác nhau

2. Map với slice (Từ điển có nhiều nghĩa)

groups := map[string][]string{
"fruits": {"apple", "banana", "orange"},
"colors": {"red", "blue", "green"},
}

💡 Giải thích:

  • Nghĩa của từ là một danh sách
  • Có thể chứa nhiều giá trị

3. Map lồng nhau (Từ điển trong từ điển)

nestedMap := map[string]map[string]int{
"math": {
"algebra": 90,
"geometry": 85,
},
"science": {
"physics": 95,
"chemistry": 88,
},
}

💡 Giải thích:

  • Nghĩa của từ là một từ điển khác
  • Giúp tổ chức dữ liệu phức tạp

Ví dụ thực tế 🎯

1. Cache đơn giản (Bộ nhớ tạm)

type Cache struct {
data map[string]interface{}
}

func NewCache() *Cache {
return &Cache{
data: make(map[string]interface{}),
}
}

func (c *Cache) Set(key string, value interface{}) {
c.data[key] = value
}

func (c *Cache) Get(key string) (interface{}, bool) {
value, exists := c.data[key]
return value, exists
}

💡 Giải thích:

  • Lưu tạm dữ liệu để truy cập nhanh
  • Có thể lưu bất kỳ kiểu dữ liệu nào

2. Đếm tần suất (Đếm số lần xuất hiện)

func countFrequency(text string) map[rune]int {
freq := make(map[rune]int)
for _, char := range text {
freq[char]++
}
return freq
}

💡 Giải thích:

  • Đếm số lần mỗi ký tự xuất hiện
  • Dùng map để lưu kết quả

Best Practices (Cách sử dụng tốt) 🌟

1. Khởi tạo map đúng cách

// ✅ Tốt
scores := make(map[string]int)

// ❌ Không tốt
var scores map[string]int
scores["Alice"] = 90 // Lỗi!

💡 Lý do: Tránh lỗi khi thêm từ mới

2. Kiểm tra từ tồn tại

// ✅ Tốt
if score, exists := scores["Alice"]; exists {
fmt.Println(score)
}

// ❌ Không tốt
score := scores["Alice"] // Không biết từ có tồn tại không

💡 Lý do: Tránh lỗi khi tra từ không tồn tại

3. Chọn kiểu dữ liệu phù hợp

// ✅ Tốt
scores := make(map[string]int, 100) // Dự kiến 100 từ

// ❌ Không tốt
scores := make(map[string]int) // Không dự kiến số lượng

💡 Lý do: Tối ưu hiệu suất khi có nhiều từ

Những lỗi thường gặp và cách sửa 🔧

  1. Lỗi: Thêm từ vào map chưa khởi tạo

    // ❌ Sai
    var scores map[string]int
    scores["Alice"] = 90 // Lỗi!

    // ✅ Đúng
    scores := make(map[string]int)
    scores["Alice"] = 90
  2. Lỗi: Không kiểm tra từ tồn tại

    // ❌ Sai
    score := scores["Alice"] // Có thể là 0

    // ✅ Đúng
    if score, exists := scores["Alice"]; exists {
    fmt.Println(score)
    }
  3. Lỗi: Dùng map cho dữ liệu cần sắp xếp

    // ❌ Sai
    scores := make(map[string]int) // Không giữ thứ tự

    // ✅ Đúng
    type Score struct {
    Name string
    Value int
    }
    scores := []Score{} // Giữ được thứ tự

Tiếp theo 🎯

Trong bài học tiếp theo, chúng ta sẽ:

  • Tìm hiểu về structs (cấu trúc dữ liệu)
  • Học cách xử lý lỗi nâng cao
  • Khám phá cách sử dụng goroutines

💡 Lời khuyên: Hãy thử tạo và sử dụng các map đơn giản để hiểu rõ hơn về cách chúng hoạt động!