From 653ccad2e62d84966fed6e12bfb095700fb888ed Mon Sep 17 00:00:00 2001 From: kouzhigang Date: Sat, 5 Jul 2025 01:06:16 +0800 Subject: [PATCH 1/2] feat(gslice): add ContainsBy --- README.md | 2 ++ README.zh-CN.md | 2 ++ gslice/gslice.go | 14 ++++++++++++++ gslice/gslice_example_test.go | 18 ++++++++++-------- gslice/gslice_test.go | 7 +++++++ 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 33be4a7..67dcd42 100644 --- a/README.md +++ b/README.md @@ -279,6 +279,8 @@ Example2:CURD Operation ```go gslice.Contains([]int{1, 2, 3, 4, 5}, 2) // true +gslice.ContainsBy([]int{1, 2, 3, 4, 5}, func(v int) bool { return v == 2 }) +// true gslice.ContainsAny([]int{1, 2, 3, 4, 5}, 2, 6) // true gslice.ContainsAll([]int{1, 2, 3, 4, 5}, 2, 6) diff --git a/README.zh-CN.md b/README.zh-CN.md index 9e44f6d..f825b7b 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -279,6 +279,8 @@ gslice.All([]int{1, 2, 3, 4, 5}, isEven) ```go gslice.Contains([]int{1, 2, 3, 4, 5}, 2) // true +gslice.ContainsBy([]int{1, 2, 3, 4, 5}, func(v int) bool { return v == 2 }) +// true gslice.ContainsAny([]int{1, 2, 3, 4, 5}, 2, 6) // true gslice.ContainsAll([]int{1, 2, 3, 4, 5}, 2, 6) diff --git a/gslice/gslice.go b/gslice/gslice.go index d213a89..9ad5a8a 100644 --- a/gslice/gslice.go +++ b/gslice/gslice.go @@ -300,6 +300,20 @@ func Contains[T comparable](s []T, v T) bool { return false } +// ContainsBy returns whether any element of the slice s satisfies the predicate f. +// +// πŸš€ EXAMPLE: +// +// ContainsBy([]int{1, 2, 3}, func(x int) bool { return x > 2 })) ⏩ true +// ContainsBy([]int{1, 2, 3}, func(x int) bool { return x > 3 })) ⏩ false +// ContainsBy([]int{1, 2, 3}, gvalue.IsZero[int])) ⏩ false +// ContainsBy([]int{1, 2, 3}, gvalue.IsNotZero[int])) ⏩ true +// +// πŸ’‘ AKA: Any +func ContainsBy[T any](s []T, f func(T) bool) bool { + return Any(s, f) +} + // ContainsAny returns whether any of given elements occur in slice. // // πŸš€ EXAMPLE: diff --git a/gslice/gslice_example_test.go b/gslice/gslice_example_test.go index 7c5a1c7..f900139 100644 --- a/gslice/gslice_example_test.go +++ b/gslice/gslice_example_test.go @@ -32,14 +32,15 @@ func Example() { fmt.Println(All([]int{1, 2, 3, 4, 5}, isEven)) // false // CRUD operation - fmt.Println(Contains([]int{1, 2, 3, 4, 5}, 2)) // true - fmt.Println(ContainsAny([]int{1, 2, 3, 4, 5}, 2, 6)) // true - fmt.Println(ContainsAll([]int{1, 2, 3, 4, 5}, 2, 6)) // false - fmt.Println(Index([]int{1, 2, 3, 4, 5}, 3).Value()) // 2 - fmt.Println(Find([]int{1, 2, 3, 4, 5}, isEven).Value()) // 2 - fmt.Println(First([]int{1, 2, 3, 4, 5}).Value()) // 1 - fmt.Println(Get([]int{1, 2, 3, 4, 5}, 1).Value()) // 2 - fmt.Println(Get([]int{1, 2, 3, 4, 5}, -1).Value()) // 5 + fmt.Println(Contains([]int{1, 2, 3, 4, 5}, 2)) // true + fmt.Println(ContainsBy([]int{1, 2, 3, 4, 5}, func(v int) bool { return v == 2 })) // true + fmt.Println(ContainsAny([]int{1, 2, 3, 4, 5}, 2, 6)) // true + fmt.Println(ContainsAll([]int{1, 2, 3, 4, 5}, 2, 6)) // false + fmt.Println(Index([]int{1, 2, 3, 4, 5}, 3).Value()) // 2 + fmt.Println(Find([]int{1, 2, 3, 4, 5}, isEven).Value()) // 2 + fmt.Println(First([]int{1, 2, 3, 4, 5}).Value()) // 1 + fmt.Println(Get([]int{1, 2, 3, 4, 5}, 1).Value()) // 2 + fmt.Println(Get([]int{1, 2, 3, 4, 5}, -1).Value()) // 5 // Partion operation fmt.Println(Range(1, 5)) // [1, 2, 3, 4] @@ -97,6 +98,7 @@ func Example() { // false // true // true + // true // false // 2 // 2 diff --git a/gslice/gslice_test.go b/gslice/gslice_test.go index d5c9a6d..7af7afb 100644 --- a/gslice/gslice_test.go +++ b/gslice/gslice_test.go @@ -182,6 +182,13 @@ func TestContains(t *testing.T) { assert.False(t, Contains([]int{}, 5)) } +func TestContainsBy(t *testing.T) { + assert.True(t, ContainsBy([]int{1, 2, 3}, func(x int) bool { return x > 2 })) + assert.False(t, ContainsBy([]int{1, 2, 3}, func(x int) bool { return x > 3 })) + assert.False(t, ContainsBy([]int{1, 2, 3}, gvalue.IsZero[int])) + assert.True(t, ContainsBy([]int{1, 2, 3}, gvalue.IsNotZero[int])) +} + func TestContainsAny(t *testing.T) { assert.True(t, ContainsAny([]int{0, 1, 2, 3, 4}, 0)) assert.False(t, Contains([]int{0, 1, 2, 3, 4}, 5)) From 968a140a5d21d82827692929d3ae7b7fd8e914fd Mon Sep 17 00:00:00 2001 From: kouzhigang Date: Mon, 7 Jul 2025 01:24:40 +0800 Subject: [PATCH 2/2] feat: add OrO --- goption/goption.go | 9 ++++----- goption/goption_test.go | 23 ++++++++++++----------- gresult/gresult.go | 20 ++++++++------------ gresult/gresult_test.go | 15 +++++++-------- gvalue/gvalue.go | 21 +++++++++++++++++++++ gvalue/gvalue_test.go | 10 ++++++++++ 6 files changed, 62 insertions(+), 36 deletions(-) diff --git a/goption/goption.go b/goption/goption.go index f60e31a..acc7cfb 100644 --- a/goption/goption.go +++ b/goption/goption.go @@ -47,8 +47,6 @@ import ( "encoding/json" "fmt" "reflect" - - "github.com/bytedance/gg/gvalue" ) // O represents a generic optional value: Every O is a value T([OK]), @@ -107,11 +105,11 @@ func (o O[T]) ValueOr(v T) T { // // πŸ’‘ HINT: Refer to function [github.com/bytedance/gg/gvalue.Zero] // for explanation of zero value. -func (o O[T]) ValueOrZero() T { +func (o O[T]) ValueOrZero() (v T) { if o.ok { return o.val } - return gvalue.Zero[T]() + return v } // Ptr returns a pointer that points to the internal value of optional value O[T]. @@ -158,7 +156,8 @@ func (o O[T]) IfNil(f func()) { // typ returns the string representation of type of optional value. func (o O[T]) typ() string { - typ := reflect.TypeOf(gvalue.Zero[T]()) + var v T + typ := reflect.TypeOf(v) if typ == nil { return "any" } diff --git a/goption/goption_test.go b/goption/goption_test.go index bd326a4..d3b39dd 100644 --- a/goption/goption_test.go +++ b/goption/goption_test.go @@ -20,14 +20,14 @@ import ( "strconv" "testing" - "github.com/bytedance/gg/gptr" "github.com/bytedance/gg/internal/assert" ) func TestOf(t *testing.T) { _ = Of(1, true) _ = OfPtr((*int)(nil)) - _ = OfPtr(gptr.Of(1)) + v := 1 + _ = OfPtr(&v) } func TestOValue(t *testing.T) { @@ -36,8 +36,8 @@ func TestOValue(t *testing.T) { assert.Equal(t, 10, Of(10, true).Value()) assert.Equal(t, 10, Of(10, false).Value()) // πŸ’‘ NOTE: not recommend assert.Equal(t, 0, Of(0, false).Value()) - - assert.Equal(t, 1, OfPtr(gptr.Of(1)).Value()) + v := 1 + assert.Equal(t, 1, OfPtr(&v).Value()) assert.Equal(t, 0, OfPtr((*int)(nil)).Value()) } @@ -130,15 +130,15 @@ func TestJSON(t *testing.T) { assert.Equal(t, string(expect), string(actual)) } { - v := gptr.Of(1) - expect, _ := json.Marshal(v) - actual, _ := json.Marshal(OfPtr(v)) + v := 1 + expect, _ := json.Marshal(&v) + actual, _ := json.Marshal(OfPtr(&v)) assert.Equal(t, string(expect), string(actual)) } { - v := gptr.Of("test") - expect, _ := json.Marshal(v) - actual, _ := json.Marshal(OfPtr(v)) + v := "test" + expect, _ := json.Marshal(&v) + actual, _ := json.Marshal(OfPtr(&v)) assert.Equal(t, string(expect), string(actual)) } @@ -282,7 +282,8 @@ func TestOThen(t *testing.T) { } func TestOPtr(t *testing.T) { - assert.Equal(t, gptr.Of(10), OK(10).Ptr()) + v := 10 + assert.Equal(t, &v, OK(v).Ptr()) assert.Equal(t, nil, Nil[int]().Ptr()) // Test modify. diff --git a/gresult/gresult.go b/gresult/gresult.go index 2bd4213..cb1e843 100644 --- a/gresult/gresult.go +++ b/gresult/gresult.go @@ -51,8 +51,6 @@ import ( "reflect" "github.com/bytedance/gg/goption" - "github.com/bytedance/gg/gptr" - "github.com/bytedance/gg/gvalue" ) // R represents a generic result: Every result is a value, @@ -87,7 +85,7 @@ func OK[T any](v T) R[T] { // // ⚠️ WARNING: Passing a nil error will cause ❌PANIC❌! func Err[T any](e error) R[T] { - if gvalue.IsNil(e) { + if reflect.ValueOf(e).IsNil() { panic(fmt.Errorf("expected a non-nil error, but found nil error with type %T", e)) } return R[T]{err: e} @@ -112,11 +110,11 @@ func (r R[T]) ValueOr(v T) T { // // πŸ’‘ HINT: Refer to function [github.com/bytedance/gg/gvalue.Zero] // for explanation of zero value. -func (r R[T]) ValueOrZero() T { +func (r R[T]) ValueOrZero() (v T) { if r.err == nil { return r.val } - return gvalue.Zero[T]() + return v } // Err returns the internal error of R. @@ -154,14 +152,15 @@ func (r R[T]) IfErr(f func(error)) { } func checkErr(e error) { - if e != nil && gvalue.IsNil(e) { + if e != nil && reflect.ValueOf(e).IsNil() { panic(fmt.Errorf("error with type %T is nil", e)) } } // typ returns the string representation of type of optional value. func (r R[T]) typ() string { - typ := reflect.TypeOf(gvalue.Zero[T]()) + var v T + typ := reflect.TypeOf(v) if typ == nil { return "any" } @@ -187,7 +186,8 @@ type jsonR[T any] struct { func (r R[T]) MarshalJSON() ([]byte, error) { jr := jsonR[T]{} if r.err != nil { - jr.Err = gptr.Of(r.err.Error()) + msg := r.err.Error() + jr.Err = &msg } else { jr.Val = &r.val } @@ -216,14 +216,10 @@ func (r *R[T]) UnmarshalJSON(data []byte) error { } if jr.Err == nil && jr.Val == nil { - r.val = gvalue.Zero[T]() - r.err = nil } else if jr.Err != nil { - r.val = gvalue.Zero[T]() r.err = errors.New(*jr.Err) } else { r.val = *jr.Val - r.err = nil } return nil diff --git a/gresult/gresult_test.go b/gresult/gresult_test.go index 579516b..6763c7f 100644 --- a/gresult/gresult_test.go +++ b/gresult/gresult_test.go @@ -23,7 +23,6 @@ import ( "testing" "github.com/bytedance/gg/goption" - "github.com/bytedance/gg/gvalue" "github.com/bytedance/gg/internal/assert" ) @@ -170,12 +169,12 @@ func TestJSON(t *testing.T) { var after1 R[int] err = json.Unmarshal(bs, &after1) assert.NotNil(t, err) - assert.Equal(t, gvalue.Zero[R[int]](), after1) + assert.Equal(t, OK(0), after1) var after2 R[float64] err = json.Unmarshal(bs, &after2) assert.NotNil(t, err) - assert.Equal(t, gvalue.Zero[R[float64]](), after2) + assert.Equal(t, OK(0.0), after2) var after3 R[string] err = json.Unmarshal(bs, &after3) @@ -205,13 +204,13 @@ func TestJSON(t *testing.T) { var r R[string] err := json.Unmarshal([]byte(`{}`), &r) assert.Nil(t, err) - assert.Equal(t, gvalue.Zero[R[string]](), r) + assert.Equal(t, OK(""), r) } { // Unmarshal empty: `null` var r R[string] err := json.Unmarshal([]byte(`null`), &r) assert.Nil(t, err) - assert.Equal(t, gvalue.Zero[R[string]](), r) + assert.Equal(t, OK(""), r) } { // Unmarshal error var r R[string] @@ -224,7 +223,7 @@ func TestJSON(t *testing.T) { err := json.Unmarshal([]byte(`{"value":"test","error":"test"}`), &r) assert.NotNil(t, err) t.Log(err) - assert.Equal(t, gvalue.Zero[R[string]](), r) + assert.Equal(t, OK(""), r) } { @@ -234,7 +233,7 @@ func TestJSON(t *testing.T) { var r R[bool] err := json.Unmarshal([]byte(`false`), &r) assert.NotNil(t, err) - assert.Equal(t, gvalue.Zero[R[bool]](), r) + assert.Equal(t, OK(false), r) } { // Unmarshal illegal: `` @@ -243,7 +242,7 @@ func TestJSON(t *testing.T) { var r R[string] err := json.Unmarshal([]byte(``), &r) assert.NotNil(t, err) - assert.Equal(t, gvalue.Zero[R[string]](), r) + assert.Equal(t, OK(""), r) } // Struct field { diff --git a/gvalue/gvalue.go b/gvalue/gvalue.go index 71c3d5e..b69e7e3 100644 --- a/gvalue/gvalue.go +++ b/gvalue/gvalue.go @@ -33,6 +33,7 @@ package gvalue import ( "reflect" + "github.com/bytedance/gg/goption" "github.com/bytedance/gg/internal/constraints" ) @@ -67,6 +68,26 @@ func Or[T comparable](vals ...T) (v T) { return } +// OrO returns the first non-zero value of inputs. +// If all values are zero, return [goption.Nil]. +// +// πŸš€ EXAMPLE: +// +// OrO(false, true) ⏩ goption.OK(true) +// OrO(0, 1, 2) ⏩ goption.OK(1) +// OrO("", "1", "2") ⏩ goption.OK("1") +// OrO(0, 0, 0) ⏩ goption.Nil[int]() +// OrO("", "", "") ⏩ goption.Nil[string]() +func OrO[T comparable](vals ...T) goption.O[T] { + var zero T + for _, val := range vals { + if val != zero { + return goption.OK(val) + } + } + return goption.Nil[T]() +} + // Max returns the maximum value of inputs. // // πŸš€ EXAMPLE: diff --git a/gvalue/gvalue_test.go b/gvalue/gvalue_test.go index db82d6f..739c4b5 100644 --- a/gvalue/gvalue_test.go +++ b/gvalue/gvalue_test.go @@ -21,6 +21,7 @@ import ( "testing" "unsafe" + "github.com/bytedance/gg/goption" "github.com/bytedance/gg/internal/assert" ) @@ -42,6 +43,15 @@ func TestOr(t *testing.T) { assert.Equal(t, 0, Or[int]()) } +func TestOrO(t *testing.T) { + assert.Equal(t, goption.OK(true), OrO(false, false, true)) + assert.Equal(t, goption.OK(1), OrO(0, 1, 2)) + assert.Equal(t, goption.OK("1"), OrO("", "1", "2")) + assert.Equal(t, goption.Nil[int](), OrO(0, 0, 0)) + assert.Equal(t, goption.Nil[string](), OrO("", "", "")) + assert.Equal(t, goption.Nil[int](), OrO[int]()) +} + func TestMin(t *testing.T) { assert.Equal(t, 1, Min(1, 2)) assert.Equal(t, 2, Min(2))