กระรอกดินต้นฉบับกำลังใช้แว่นขยายรวมการ์ดงานที่เหมือนกันและวางติดกันบนโต๊ะสีมิ้นต์

Go slices.CompactFunc: รวมข้อมูลซ้ำที่ติดกันด้วยกติกาของเรา

พี่โชว์Engineering

ถ้าข้อมูลซ้ำกันแต่ == ไม่ใช่กติกาที่ธุรกิจต้องการ เช่น ชื่อที่ต่างกันแค่ตัวพิมพ์ใหญ่เล็ก slices.CompactFunc ช่วยรวม สมาชิกที่เท่ากันและอยู่ติดกัน โดยให้เราเขียนฟังก์ชันเปรียบเทียบเองครับ

WHY: deduplicate โดยไม่ทิ้งความหมายของโดเมน

slices.Compact ใช้ == จึงเหมาะกับค่าที่ comparable และนิยามความเท่ากันตรงไปตรงมา แต่ระบบจริงอาจถือว่า API กับ api คือคิวเดียวกัน หรือ record สองตัวเท่ากันเมื่อมีรหัสเดียวกัน การแยกกติกานี้เป็นฟังก์ชันทำให้คนอ่านเห็นเจตนาชัดและทดสอบ edge case ได้

ข้อสำคัญคือ CompactFunc ไม่ค้นหาค่าซ้ำทั่ว slice มันรวมเฉพาะ “ก้อนที่ติดกัน” เหมือนพนักงานรวมใบสั่งโต๊ะเดียวกันที่วางต่อกันบนเคาน์เตอร์ ถ้ากระจายอยู่คนละจุด ต้อง sort/group ก่อน หรือใช้ map เมื่อไม่ต้องรักษาลำดับ

HOW: เก็บตัวแรกของแต่ละก้อน

package main

import (
	"fmt"
	"slices"
	"strings"
)

func main() {
	queue := []string{"API", "api", "Billing", "billing", "Search"}
	got := slices.CompactFunc(queue, strings.EqualFold)

	want := []string{"API", "Billing", "Search"}
	if !slices.Equal(got, want) {
		panic(fmt.Sprintf("got %v want %v", got, want))
	}
	if got[0] != "API" {
		panic("CompactFunc must keep the first item in a run")
	}
	fmt.Println(got)
}

ฟังก์ชันแก้ slice เดิมและคืน slice ที่สั้นลง ไม่ได้สร้างรายการใหม่ทั้งก้อน ตั้งแต่ Go รุ่นปัจจุบัน ช่องท้ายที่หลุดจากผลจะถูก zero เพื่อไม่ค้าง reference โดยไม่จำเป็น แต่ caller ที่ถือ backing array เดียวกันยังอาจเห็นการเปลี่ยนแปลง จึง slices.Clone ก่อนถ้าต้นฉบับต้องคงเดิม

เลือกเครื่องมือให้ตรงงาน

สถานการณ์ทางเลือก
ค่าซ้ำอยู่ติดกันและใช้ ==slices.Compact
ค่าซ้ำอยู่ติดกันแต่ equality เป็นกติกาธุรกิจslices.CompactFunc
ค่าซ้ำกระจายและลำดับไม่สำคัญmap/set
ค่าซ้ำกระจายแต่ต้องรักษาลำดับพบครั้งแรกmap ช่วยจำ + slice ผลลัพธ์

Recap

  • ยืนยันก่อนว่าค่าซ้ำอยู่ติดกัน
  • เขียน equality ให้เป็นฟังก์ชันสั้นและไม่มี side effect
  • จำว่าเก็บตัวแรกของแต่ละก้อน
  • clone ก่อนเมื่อห้ามแก้ backing array เดิม