Open3

よく使う実装

engineer rebornengineer reborn

目次

  • 重点的に
    • slice操作
    • make
    • recover
    • defer
    • go routine
    • context
    • ntewrelic
    • set
  • lo 関数
  • slice
  • 複数のデータをmapで処理
  • ログだし
  • エラーコード echo レスポンス
  • setupDBなどの innerに定義する書き方
  • errgroup.Groupの書き方
engineer rebornengineer reborn

lo

  • lo系共通イメージ

    • スライスなどの要素が1つずつ処理の対象になるイメージ
    • 関数の引数は、関数が何を返すかによって変わる
      • Map
        • スライスを返すのでスライスの要素になるように
      • SliceToMap
        • mapを返すので key, valueを返り値
      • Find
        • 判定処理に使うために booleanを返り値に
  • Map

    • これはスライスを新しいスライスにする関数
package main

import (
	"fmt"
	"github.com/samber/lo"
)

type User struct {
	ID   int
	Name string
}

func main() {
	users := []User{
		{ID: 101, Name: "Alice"},
		{ID: 102, Name: "Bob"},
		{ID: 103, Name: "Charlie"},
	}

	// lo.Map を使って、UserスライスからIDのスライスを抽出
	userIDs := lo.Map(users, func(u User, _ int) int {
		return u.ID
	})

	fmt.Println("Original Users:", users)
	fmt.Println("Mapped User IDs:", userIDs) // Output: Mapped User IDs: [101 102 103]
}

### こんな感じでも良い

// You can edit this code!
// Click here and start typing.
package main

import (
	"fmt"

	"github.com/samber/lo"
)

type User struct {
	ID   int
	Name string
}

type KeyValue struct {
	Key   int
	Value string
}

func main() {
	users := []User{
		{ID: 101, Name: "Alice"},
		{ID: 102, Name: "Bob"},
		{ID: 103, Name: "Chaline"},
	}

	userIds := lo.Map(users, func(u User, index int) KeyValue {
		return KeyValue{Key: index, Value: u.Name}
	})

	fmt.Println("Hello, 世界", userIds)
}
  • Must
    • エラーが発生するならpanicを起こす
      • panicがおこらないなら、jstは必ずある
package main

import (
	"fmt"
	"time"

	"github.com/samber/lo"
)

func main() {
	// 存在するタイムゾーンのロード(成功するケース)
	jst := lo.Must(time.LoadLocation("Asia/Tokyo"))
	fmt.Println("Successfully loaded JST:", jst.String())

	// 存在しないタイムゾーンのロード(失敗するケース - この行はパニックするためコメントアウト)
	// nonExistentLocation := lo.Must(time.LoadLocation("NonExistent/Location"))
	// fmt.Println("Loaded NonExistent Location:", nonExistentLocation.String())

	fmt.Println("Program finished successfully if no panic occurred for invalid location.")
}
  • slicetoMap
    • sliceは配列のようなもの
    • mapにする
    • 関数の返り値はkey, valueにする
      • ex) (string, int)
package main

import (
	"fmt"
	"github.com/samber/lo"
)

type Product struct {
	ID   int
	Name string
	Price int
}

func main() {
	products := []Product{
		{ID: 1, Name: "Apple", Price: 100},
		{ID: 2, Name: "Banana", Price: 50},
		{ID: 3, Name: "Orange", Price: 120},
	}

	// lo.SliceToMap を使って、ProductスライスからNameをキー、IDを値とするマップを作成
	productIDMap := lo.SliceToMap(products, func(p Product) (string, int) {
		return p.Name, p.ID
	})

	fmt.Println("Original Products:", products)
	fmt.Println("Product ID Map (Name -> ID):", productIDMap)
	// Output: Product ID Map (Name -> ID): map[Apple:1 Banana:2 Orange:3]
}
  • Find
    • 1つ対象を見つけたら処理が終わる
package main

import (
	"fmt"
	"github.com/samber/lo"
)

func main() {
	numbers := []int{10, 25, 30, 45, 50}

	// lo.Find を使って、30以上の最初の偶数を見つける
	if foundNum, found := lo.Find(numbers, func(n int) bool {
		return n >= 30 && n%2 == 0
	}) {
		fmt.Printf("Found number: %d\n", foundNum) // Output: Found number: 30
	} else {
		fmt.Println("Number not found.")
	}

	// 見つからないケース
	if _, found := lo.Find(numbers, func(n int) bool {
		return n > 100
	}) {
		fmt.Println("Found number (should not happen).")
	} else {
		fmt.Println("Number > 100 not found (expected).")
	}
}
  • Ptr
package main

import (
	"fmt"
	"github.com/samber/lo"
)

func main() {
	value := "hello"
	ptrValue := lo.ToPtr(value) // string型の値 "hello" へのポインタを取得

	fmt.Printf("Original value: %s\n", value)
	fmt.Printf("Pointer value: %s (address: %p)\n", *ptrValue, ptrValue)

	num := 42
	ptrNum := lo.ToPtr(num) // int型の値 42 へのポインタを取得

	fmt.Printf("Original number: %d\n", num)
	fmt.Printf("Pointer number: %d (address: %p)\n", *ptrNum, ptrNum)
}
engineer rebornengineer reborn

slice

  • slice

    • 要素数が固定ではない、配列じゃ
  • extract

package main

import (
	"fmt"
	"github.com/samber/lo"
)

type Item struct {
	Name  string
	Price int
}

func main() {
	items := []Item{
		{"Laptop", 1200},
		{"Mouse", 25},
		{"Keyboard", 75},
	}

	// アイテムのスライスから価格のスライスを抽出 (Extractの一般的な意味)
	prices := lo.Map(items, func(item Item, _ int) int {
		return item.Price
	})

	fmt.Println("Original Items:", items)
	fmt.Println("Extracted Prices:", prices) // Output: Extracted Prices: [1200 25 75]
}
  • map
package main

import (
	"fmt"
	"github.com/samber/lo"
)

type CompanyID int // company.ID のようなカスタム型を模擬

func main() {
	companyIDs := []CompanyID{1001, 1002, 1003, 2005}

	// lo.Map を使って []CompanyID を []int に変換
	intCompanyIDs := lo.Map(companyIDs, func(id CompanyID, _ int) int {
		return int(id)
	})

	fmt.Println("Original CompanyIDs:", companyIDs)
	fmt.Println("Casted (mapped) to Ints:", intCompanyIDs) // Output: Casted (mapped) to Ints: [1001 1002 1003 2005]
}
  • exclude
package main

import (
	"fmt"
	"github.com/samber/lo"
)

func main() {
	// userIDs := []int{1, 2, 3, 4, 5}
	// excludeUserIDs := []int{3, 5}
	// slice.Exclude(userIDs, excludeUserIDs) // -> [1, 2, 4]

	mainSlice := []int{10, 20, 30, 40, 50, 60}
	excludeSlice := []int{20, 50, 70} // 70はmainSliceにないが、問題ない

	// lo.Filter と lo.Contains を使って除外
	resultFiltered := lo.Filter(mainSlice, func(item int, _ int) bool {
		return !lo.Contains(excludeSlice, item)
	})
	fmt.Println("Original Slice:", mainSlice)
	fmt.Println("Exclude Slice:", excludeSlice)
	fmt.Println("Result (using lo.Filter):", resultFiltered) // Output: Result (using lo.Filter): [10 30 40 60]

	// または、lo.Without (複数の引数を取るが、動的な排除リストには lo.Filter が便利)
	// lo.Withoutは固定された要素を削除するのに便利
	resultWithout := lo.Without(mainSlice, 20, 50)
	fmt.Println("Result (using lo.Without):", resultWithout) // Output: Result (using lo.Without): [10 30 40 60]

	// 複数のスライスからの除外をlo.Withoutで表現するには工夫が必要
	// lo.Filter + lo.Contains が汎用性が高い
}
package main

import "fmt"

// account パッケージの Account と UserID 型をシミュレート
// 練習のため、ここではシンプルな構造体として定義します
type Account struct {
	ID       string // ユーザーIDをシミュレート
	Username string
	Email    string
}

type UserID string // UserID は string のエイリアスと仮定

func main() {
	// サンプルの全ユーザーデータ
	allUsers := []Account{
		{ID: "user001", Username: "Alice", Email: "alice@example.com"},
		{ID: "user002", Username: "Bob", Email: "bob@example.com"},
		{ID: "user003", Username: "Charlie", Email: "charlie@example.com"},
	}

	// slice.Extract の処理を標準的なGoのループで再現
	// Account スライスから UserID スライスを抽出する例

	var allUserIDs []UserID // UserID を格納するための新しいスライスを宣言

	// allUsers スライスの各要素をループ処理
	// func(_ int, s account.Account) account.UserID { return s.ID }
	// に相当する処理を for ループ内で行います
	for _, user := range allUsers {
		// 各 Account オブジェクトから ID (string) を取り出し、
		// それを UserID 型に変換して新しいスライスに追加
		allUserIDs = append(allUserIDs, UserID(user.ID))
	}

	// 結果の表示
	fmt.Println("元のユーザーデータ:")
	for _, user := range allUsers {
		fmt.Printf("  ID: %s, Username: %s, Email: %s\n", user.ID, user.Username, user.Email)
	}

	fmt.Println("\n抽出されたユーザーID:")
	for _, id := range allUserIDs {
		fmt.Printf("  %s\n", id)
	}

	// 期待される出力:
	// 抽出されたユーザーID:
	//   user001
	//   user002
	//   user003
}