กระรอกดินต้นฉบับกำลังจับคู่เมล็ดกับช่องเก็บสีต่างกันบนโต๊ะสว่าง

Go maps.Collect: สร้าง map จาก iterator คู่ key-value

พี่โชว์Engineering

เมื่อข้อมูลไหลมาเป็นคู่ key-value ผ่าน iter.Seq2 เราสร้าง map ด้วย maps.Collect ได้โดยไม่ต้องเขียนลูป boilerplate ครับ จุดสำคัญคือถ้า key ซ้ำ ค่าหลังสุดจะทับค่าก่อนหน้า จึงต้องตัดสินใจว่า behavior นี้ตรงกับกติกาธุรกิจหรือไม่

WHY: ติดป้ายช่องเก็บ แต่ป้ายซ้ำต้องมีกติกา

เหมือนพนักงานรับของที่วางสินค้าในช่องตามป้าย ถ้าป้ายเดิมมาถึงอีกครั้ง เขาจะเปลี่ยนของในช่องนั้น ไม่ได้สร้างช่องใหม่ ความสะดวกของ collector จึงไม่แทน validation หาก duplicate หมายถึงข้อมูลเสียต้องตรวจและคืน error ก่อน collect

package main

import (
	"iter"
	"maps"
	"testing"
)

func entries() iter.Seq2[string, int] {
	return func(yield func(string, int) bool) {
		for _, pair := range []struct {
			key string
			val int
		}{{"mint", 1}, {"forest", 2}, {"mint", 3}} {
			if !yield(pair.key, pair.val) {
				return
			}
		}
	}
}

func TestCollect(t *testing.T) {
	got := maps.Collect(entries())
	if got["mint"] != 3 || got["forest"] != 2 || len(got) != 2 {
		t.Fatalf("unexpected map: %#v", got)
	}
	empty := maps.Collect(func(func(string, int) bool) {})
	if len(empty) != 0 {
		t.Fatalf("expected empty map: %#v", empty)
	}
}

Collection ไม่ใช่ validation

อย่าใช้ค่าทับกันเป็น default หาก config ซ้ำอาจซ่อนความผิดพลาดหรือเปิดช่องให้ input ท้ายสุดเปลี่ยน policy ทำ wrapper ที่เก็บ seen แล้วหยุดพร้อม error ก่อน Collect หรือเขียนลูปตรง ๆ เมื่อ error เป็นส่วนหนึ่งของ contract

Map ที่ได้ไม่รักษาลำดับ และ element ที่เป็น pointer ยังชี้ object เดิม หากต้อง deterministic output ให้ sort key ตอน serialize ไม่ควรพึ่ง iteration order

Recap

  • ใช้ maps.Collect กับ iter.Seq2 ที่ไว้ใจ duplicate policy แล้ว
  • จำว่า key ซ้ำใช้ค่าหลังสุด
  • แยก validation/error path ก่อน collection
  • sort key เมื่อต้องการ output ที่ทำซ้ำได้