ข้ามไปยังเนื้อหา

Arrays, Slices และ Maps

ใน JavaScript/TypeScript array เป็นแบบไดนามิกโดยปริยาย และ object ทำหน้าที่เป็น map ส่วน Go แยกเรื่องเหล่านี้ออกจากกัน: arrays ขนาดคงที่, slices แบบไดนามิก (ตัวที่คุณจะใช้ทุกวัน) และ maps

array ของ Go มีขนาดคงที่ฝังอยู่ใน type เลย ในทางปฏิบัติคุณแทบไม่ได้ใช้ array ตรง ๆ ตัวที่ใช้จริงคือ slice — เวอร์ชันไดนามิกที่มี array หนุนอยู่เบื้องหลัง แต่มี length และ capacity ที่ขยายได้ตามต้องการ

TypeScript
// TypeScript — always dynamic
const nums: number[] = [1, 2, 3];
nums.push(4);
console.log(nums.length); // 4
Go
// Go — slice (dynamic)
nums := []int{1, 2, 3}
nums = append(nums, 4)
fmt.Println(len(nums)) // 4
TypeScript
const letters = ["a", "b", "c", "d"];
// slice (non-mutating)
letters.slice(1, 3) // ["b", "c"]
// length
letters.length // 4
Go
letters := []string{"a", "b", "c", "d"}
// slice expression
letters[1:3] // ["b", "c"]
// length
len(letters) // 4

maps ของ Go เหมือนกับ Record<K,V> หรือ Map<K,V> ของ TypeScript คีย์สามารถเป็น type ใดก็ได้ที่เปรียบเทียบกันได้ (comparable)

TypeScript
// TypeScript
const scores: Record<string, number> = {
alice: 95,
bob: 87,
};
scores["carol"] = 91;
delete scores["bob"];
console.log(scores["alice"]);
Go
// Go
scores := map[string]int{
"alice": 95,
"bob": 87,
}
scores["carol"] = 91
delete(scores, "bob")
fmt.Println(scores["alice"])
package main
import (
"fmt"
"sort"
)
func main() {
// Slice
fruits := []string{"banana", "apple", "cherry"}
fruits = append(fruits, "date")
sort.Strings(fruits)
fmt.Println("Fruits:", fruits)
// Map
count := map[string]int{}
words := []string{"go", "is", "fun", "go", "is", "great", "go"}
for _, w := range words {
count[w]++
}
fmt.Println("Word counts:", count)
}

ตั้งแต่ Go 1.23 loop for range วนบน function ที่มี type iter.Seq[T] (หรือ iter.Seq2[K, V]) ได้ นี่คือ generator ฉบับ Go: ค่าถูกผลิตแบบ lazy ทีละตัวผ่าน yield โดย loop body เป็นผู้บริโภค package slices และ maps มี helper แบบ iterator มาให้ (slices.Values, maps.Keys, slices.Collect, slices.Sorted)

TypeScript
// TypeScript: generator + for…of
function* evens(limit: number) {
for (let n = 0; n <= limit; n += 2) yield n;
}
for (const n of evens(6)) console.log(n); // 0 2 4 6
const keys = [...new Map([["a", 1], ["b", 2]]).keys()];
Go
// Go 1.23+: a func(yield func(T) bool) is an iterator
func evens(limit int) iter.Seq[int] {
return func(yield func(int) bool) {
for n := 0; n <= limit; n += 2 {
if !yield(n) { return } // consumer broke out of the loop
}
}
}
for n := range evens(6) { fmt.Println(n) } // 0 2 4 6
// stdlib helpers return iterators; Collect/Sorted turn them back into slices
keys := slices.Sorted(maps.Keys(map[string]int{"a": 1, "b": 2})) // [a b]

การที่ callback yield คืน false คือวิธีที่ break ส่งสัญญาณกลับไปหาผู้ผลิต — ไม่มี generator object ให้เรียก .return() ต่างจาก generator ของ JS ตรงที่ iterator ของ Go เป็น function ธรรมดา จึง inline ได้ดีและกรณีทั่วไปไม่ต้อง allocate อะไรเลย และตั้งแต่ Go 1.22 ก็เขียน for i := range 10 วนบน int ได้ด้วย

collection type ใดของ Go ที่ขยายตัวได้แบบไดนามิก (เหมือน array ของ JS)?
คุณ append เข้าไปใน slice ใน Go อย่างไร?
การ lookup map แบบสองค่า (val, ok := m[key]) บอกอะไรคุณ?
zero value ที่คืนมาเมื่อเข้าถึงคีย์ int ที่ไม่มีอยู่ใน map คืออะไร?