diff --git a/README.md b/README.md index 38f469bca..e8fe77132 100644 --- a/README.md +++ b/README.md @@ -322,6 +322,8 @@ Type manipulation helpers: - [ToSlicePtr](#tosliceptr) - [FromSlicePtr](#fromsliceptr) - [FromSlicePtrOr](#fromsliceptror) +- [EqualPtr](#equalptr) +- [ComparePtr](#compareptr) - [ToAnySlice](#toanyslice) - [FromAnySlice](#fromanyslice) - [Empty](#empty) @@ -3689,6 +3691,67 @@ ptr := lo.FromSlicePtrOr([]*string{&str1, nil, &str2}, "fallback value") [[play](https://go.dev/play/p/CuXGVzo9G65)] +### EqualPtr + +Checks equality of two pointers by their dereferenced values, handling nil cases. + +```go +ptr42 := lo.ToPtr(42) +ptr42_2 := lo.ToPtr(42) +ptr100 := lo.ToPtr(100) +ptrNil := (*int)(nil) + +equal1 := lo.EqualPtr(ptr42, ptr42_2) +// equal1: true (both point to 42) + +equal2 := lo.EqualPtr(ptr42, ptr100) +// equal2: false (42 != 100) + +equal3 := lo.EqualPtr(ptr42, ptrNil) +// equal3: false (one is nil) + +equal4 := lo.EqualPtr(ptrNil, ptrNil) +// equal4: true (both are nil) +``` + +[[play](https://go.dev/play/p/qMzVWWBMbNX)] + +### ComparePtr + +ComparePtr compares two pointers and returns: +- 0 if both are nil or *a == *b +- -1 if a is nil (and b is not) or *a < *b +- 1 if b is nil (and a is not) or *a > *b + +Nil is treated as less than any non-nil value. + +```go +ptr42 := lo.ToPtr(42) +ptr42_2 := lo.ToPtr(42) +ptr100 := lo.ToPtr(100) +ptrNil := (*int)(nil) + +equal1 := lo.ComparePtr(ptr42, ptr42_2) +// equal1: 0 (42 == 42) + +equal2 := lo.ComparePtr(ptr42, ptr100) +// equal2: -1 (42 < 100) + +equal3 := lo.ComparePtr(ptr100, ptr42) +// equal3: 1 (100 > 42) + +equal4 := lo.ComparePtr(ptr42, ptrNil) +// equal4: 1 (42 > nil) + +equal5 := lo.ComparePtr(ptrNil, ptr42) +// equal5: -1 (nil < 42) + +equal6 := lo.ComparePtr(ptrNil, ptrNil) +// equal6: 0 (nil == nil) +``` + +[[play](https://go.dev/play/p/-VYPXhng6lB)] + ### ToAnySlice Returns a slice with all elements mapped to `any` type. diff --git a/docs/data/core-coalesce.md b/docs/data/core-coalesce.md index 738e1eb03..07c7f3175 100644 --- a/docs/data/core-coalesce.md +++ b/docs/data/core-coalesce.md @@ -1,7 +1,7 @@ --- name: Coalesce slug: coalesce -sourceRef: type_manipulation.go#L153 +sourceRef: type_manipulation.go#L157 category: core subCategory: type signatures: diff --git a/docs/data/core-coalescemap.md b/docs/data/core-coalescemap.md index 6f0eb8ba1..a1ba9b179 100644 --- a/docs/data/core-coalescemap.md +++ b/docs/data/core-coalescemap.md @@ -1,7 +1,7 @@ --- name: CoalesceMap slug: coalescemap -sourceRef: type_manipulation.go#L196 +sourceRef: type_manipulation.go#L200 category: core subCategory: type signatures: diff --git a/docs/data/core-coalescemaporempty.md b/docs/data/core-coalescemaporempty.md index e4b098336..87328ce72 100644 --- a/docs/data/core-coalescemaporempty.md +++ b/docs/data/core-coalescemaporempty.md @@ -1,7 +1,7 @@ --- name: CoalesceMapOrEmpty slug: coalescemaporempty -sourceRef: type_manipulation.go#L207 +sourceRef: type_manipulation.go#L211 category: core subCategory: type signatures: diff --git a/docs/data/core-coalesceorempty.md b/docs/data/core-coalesceorempty.md index d13990054..42966488f 100644 --- a/docs/data/core-coalesceorempty.md +++ b/docs/data/core-coalesceorempty.md @@ -1,7 +1,7 @@ --- name: CoalesceOrEmpty slug: coalesceorempty -sourceRef: type_manipulation.go#L167 +sourceRef: type_manipulation.go#L171 category: core subCategory: type signatures: diff --git a/docs/data/core-coalesceslice.md b/docs/data/core-coalesceslice.md index 604eb8ec4..63d696936 100644 --- a/docs/data/core-coalesceslice.md +++ b/docs/data/core-coalesceslice.md @@ -1,7 +1,7 @@ --- name: CoalesceSlice slug: coalesceslice -sourceRef: type_manipulation.go#L174 +sourceRef: type_manipulation.go#L178 category: core subCategory: type signatures: diff --git a/docs/data/core-coalescesliceorempty.md b/docs/data/core-coalescesliceorempty.md index c2c9bd434..9326c1846 100644 --- a/docs/data/core-coalescesliceorempty.md +++ b/docs/data/core-coalescesliceorempty.md @@ -1,7 +1,7 @@ --- name: CoalesceSliceOrEmpty slug: coalescesliceorempty -sourceRef: type_manipulation.go#L185 +sourceRef: type_manipulation.go#L189 category: core subCategory: type signatures: diff --git a/docs/data/core-compareptr.md b/docs/data/core-compareptr.md new file mode 100644 index 000000000..a292ba821 --- /dev/null +++ b/docs/data/core-compareptr.md @@ -0,0 +1,48 @@ +--- +name: ComparePtr +slug: compareptr +sourceRef: type_manipulation.go#L229 +category: core +subCategory: type +signatures: + - "func ComparePtr[T constraints.Ordered](a, b *T) bool" +variantHelpers: + - core#type#compareptr +similarHelpers: + - core#type#equalptr + - core#type#fromptr + - core#type#toptr +position: 110 +--- + +ComparePtr compares two pointers and returns: +- 0 if both are nil or *a == *b +- -1 if a is nil (and b is not) or *a < *b +- 1 if b is nil (and a is not) or *a > *b + +Nil is treated as less than any non-nil value. + +```go +ptr42 := lo.ToPtr(42) +ptr42_2 := lo.ToPtr(42) +ptr100 := lo.ToPtr(100) +ptrNil := (*int)(nil) + +equal1 := lo.ComparePtr(ptr42, ptr42_2) +// equal1: 0 (42 == 42) + +equal2 := lo.ComparePtr(ptr42, ptr100) +// equal2: -1 (42 < 100) + +equal3 := lo.ComparePtr(ptr100, ptr42) +// equal3: 1 (100 > 42) + +equal4 := lo.ComparePtr(ptr42, ptrNil) +// equal4: 1 (42 > nil) + +equal5 := lo.ComparePtr(ptrNil, ptr42) +// equal5: -1 (nil < 42) + +equal6 := lo.ComparePtr(ptrNil, ptrNil) +// equal6: 0 (nil == nil) +``` \ No newline at end of file diff --git a/docs/data/core-empty.md b/docs/data/core-empty.md index 6573e80c6..02833c8a1 100644 --- a/docs/data/core-empty.md +++ b/docs/data/core-empty.md @@ -1,7 +1,7 @@ --- name: Empty slug: empty -sourceRef: type_manipulation.go#L132 +sourceRef: type_manipulation.go#L136 category: core subCategory: type signatures: diff --git a/docs/data/core-emptyabletoptr.md b/docs/data/core-emptyabletoptr.md index f42343da2..b1d3ef2b4 100644 --- a/docs/data/core-emptyabletoptr.md +++ b/docs/data/core-emptyabletoptr.md @@ -1,7 +1,7 @@ --- name: EmptyableToPtr slug: emptyabletoptr -sourceRef: type_manipulation.go#L41 +sourceRef: type_manipulation.go#L45 category: core subCategory: type signatures: diff --git a/docs/data/core-equalptr.md b/docs/data/core-equalptr.md new file mode 100644 index 000000000..34782b838 --- /dev/null +++ b/docs/data/core-equalptr.md @@ -0,0 +1,37 @@ +--- +name: EqualPtr +slug: equalptr +sourceRef: type_manipulation.go#L220 +category: core +subCategory: type +signatures: + - "func EqualPtr[T comparable](a, b *T) bool" +variantHelpers: + - core#type#equalptr +similarHelpers: + - core#type#compareptr + - core#type#fromptr + - core#type#toptr +position: 100 +--- + +Compares two pointers: returns true if both are nil or both point to equal values, false if only one is nil or values differ. + +```go +ptr42 := lo.ToPtr(42) +ptr42_2 := lo.ToPtr(42) +ptr100 := lo.ToPtr(100) +ptrNil := (*int)(nil) + +equal1 := lo.EqualPtr(ptr42, ptr42_2) +// equal1: true (both point to 42) + +equal2 := lo.EqualPtr(ptr42, ptr100) +// equal2: false (42 != 100) + +equal3 := lo.EqualPtr(ptr42, ptrNil) +// equal3: false (one is nil) + +equal4 := lo.EqualPtr(ptrNil, ptrNil) +// equal4: true (both are nil) +``` \ No newline at end of file diff --git a/docs/data/core-fromanyslice.md b/docs/data/core-fromanyslice.md index 1a2149048..8df1e5938 100644 --- a/docs/data/core-fromanyslice.md +++ b/docs/data/core-fromanyslice.md @@ -1,7 +1,7 @@ --- name: FromAnySlice slug: fromanyslice -sourceRef: type_manipulation.go#L118 +sourceRef: type_manipulation.go#L122 category: core subCategory: type signatures: diff --git a/docs/data/core-fromptr.md b/docs/data/core-fromptr.md index bacedf4fe..72b956dde 100644 --- a/docs/data/core-fromptr.md +++ b/docs/data/core-fromptr.md @@ -1,7 +1,7 @@ --- name: FromPtr slug: fromptr -sourceRef: type_manipulation.go#L53 +sourceRef: type_manipulation.go#L57 category: core subCategory: type signatures: diff --git a/docs/data/core-fromptror.md b/docs/data/core-fromptror.md index 120f01baa..f9f1c3424 100644 --- a/docs/data/core-fromptror.md +++ b/docs/data/core-fromptror.md @@ -1,7 +1,7 @@ --- name: FromPtrOr slug: fromptror -sourceRef: type_manipulation.go#L63 +sourceRef: type_manipulation.go#L67 category: core subCategory: type signatures: diff --git a/docs/data/core-fromsliceptr.md b/docs/data/core-fromsliceptr.md index 5cb70bda0..b074ece80 100644 --- a/docs/data/core-fromsliceptr.md +++ b/docs/data/core-fromsliceptr.md @@ -1,7 +1,7 @@ --- name: FromSlicePtr slug: fromsliceptr -sourceRef: type_manipulation.go#L85 +sourceRef: type_manipulation.go#L89 category: core subCategory: type signatures: diff --git a/docs/data/core-isempty.md b/docs/data/core-isempty.md index b4476c0c9..9085de6ec 100644 --- a/docs/data/core-isempty.md +++ b/docs/data/core-isempty.md @@ -1,7 +1,7 @@ --- name: IsEmpty slug: isempty -sourceRef: type_manipulation.go#L139 +sourceRef: type_manipulation.go#L143 category: core subCategory: type signatures: diff --git a/docs/data/core-isnil.md b/docs/data/core-isnil.md index e30db7ad2..b4149ed47 100644 --- a/docs/data/core-isnil.md +++ b/docs/data/core-isnil.md @@ -1,7 +1,7 @@ --- name: IsNil slug: isnil -sourceRef: type_manipulation.go#L7 +sourceRef: type_manipulation.go#L11 category: core subCategory: type signatures: diff --git a/docs/data/core-isnotempty.md b/docs/data/core-isnotempty.md index ac9fe6eb8..0bee4b752 100644 --- a/docs/data/core-isnotempty.md +++ b/docs/data/core-isnotempty.md @@ -1,7 +1,7 @@ --- name: IsNotEmpty slug: isnotempty -sourceRef: type_manipulation.go#L146 +sourceRef: type_manipulation.go#L150 category: core subCategory: type signatures: diff --git a/docs/data/core-isnotnil.md b/docs/data/core-isnotnil.md index 9180bf070..adac8a3a5 100644 --- a/docs/data/core-isnotnil.md +++ b/docs/data/core-isnotnil.md @@ -1,7 +1,7 @@ --- name: IsNotNil slug: isnil -sourceRef: type_manipulation.go#L22 +sourceRef: type_manipulation.go#L26 category: core subCategory: type signatures: diff --git a/docs/data/core-nil.md b/docs/data/core-nil.md index 15a8e601a..138c19576 100644 --- a/docs/data/core-nil.md +++ b/docs/data/core-nil.md @@ -1,7 +1,7 @@ --- name: Nil slug: nil -sourceRef: type_manipulation.go#L34 +sourceRef: type_manipulation.go#L38 category: core subCategory: type playUrl: https://go.dev/play/p/P2sD0PMXw4F diff --git a/docs/data/core-toanyslice.md b/docs/data/core-toanyslice.md index df1046151..003217da9 100644 --- a/docs/data/core-toanyslice.md +++ b/docs/data/core-toanyslice.md @@ -1,7 +1,7 @@ --- name: ToAnySlice slug: toanyslice -sourceRef: type_manipulation.go#L107 +sourceRef: type_manipulation.go#L111 category: core subCategory: type signatures: diff --git a/docs/data/core-toptr.md b/docs/data/core-toptr.md index 662a13c2e..7a47ea122 100644 --- a/docs/data/core-toptr.md +++ b/docs/data/core-toptr.md @@ -1,7 +1,7 @@ --- name: ToPtr slug: toptr -sourceRef: type_manipulation.go#L28 +sourceRef: type_manipulation.go#L32 category: core subCategory: type signatures: diff --git a/docs/data/core-tosliceptr.md b/docs/data/core-tosliceptr.md index 586fc9048..4a74819a5 100644 --- a/docs/data/core-tosliceptr.md +++ b/docs/data/core-tosliceptr.md @@ -1,7 +1,7 @@ --- name: ToSlicePtr slug: tosliceptr -sourceRef: type_manipulation.go#L73 +sourceRef: type_manipulation.go#L77 category: core subCategory: type signatures: diff --git a/docs/static/llms.txt b/docs/static/llms.txt index 99d5ed244..f12f6ecf3 100644 --- a/docs/static/llms.txt +++ b/docs/static/llms.txt @@ -305,6 +305,8 @@ Lo is built on a foundation of pragmatic engineering principles that balance pow - ToSlicePtr: Convert slice to slice of pointers - FromSlicePtr: Convert slice of pointers to slice - FromSlicePtrOr: Convert slice of pointers to slice or fallback +- EqualPtr: Check two pointers to equal values +- ComparePtr: Compare two `constraints.Ordered` pointer values - ToAnySlice: Convert slice to []any - FromAnySlice: Convert []any to typed slice - Empty: Get zero value of type diff --git a/type_manipulation.go b/type_manipulation.go index c2f93d735..cdaf923f9 100644 --- a/type_manipulation.go +++ b/type_manipulation.go @@ -1,6 +1,10 @@ package lo -import "reflect" +import ( + "reflect" + + "github.com/samber/lo/internal/constraints" +) // IsNil checks if a value is nil or if it's a reference type with a nil underlying value. // Play: https://go.dev/play/p/P2sD0PMXw4F @@ -212,3 +216,33 @@ func CoalesceMapOrEmpty[K comparable, V any](v ...map[K]V) map[K]V { } return map[K]V{} } + +// EqualPtr checks equality of two pointers by their dereferenced values, handling nil cases. +// Play: https://go.dev/play/p/qMzVWWBMbNX +func EqualPtr[T comparable](a, b *T) bool { + if a == nil || b == nil { + return a == b + } + return *a == *b +} + +// ComparePtr compares two pointers by their dereferenced values, handling nil cases. +// Play: https://go.dev/play/p/-VYPXhng6lB +func ComparePtr[T constraints.Ordered](a, b *T) int { + if a == nil && b == nil { + return 0 + } + if a == nil { + return -1 + } + if b == nil { + return 1 + } + if *a < *b { + return -1 + } + if *a > *b { + return 1 + } + return 0 +} diff --git a/type_manipulation_test.go b/type_manipulation_test.go index 3a54a558a..710b0f398 100644 --- a/type_manipulation_test.go +++ b/type_manipulation_test.go @@ -545,3 +545,30 @@ func TestCoalesceMapOrEmpty(t *testing.T) { is.NotNil(result10) is.Equal(map1, result10) } + +func TestEqualPtr(t *testing.T) { + t.Parallel() + is := assert.New(t) + + newInt := func(v int) *int { return &v } + + is.True(EqualPtr[int](nil, nil)) + is.False(EqualPtr[int](nil, newInt(1))) + is.False(EqualPtr[int](newInt(1), nil)) + is.True(EqualPtr[int](newInt(1), newInt(1))) + is.False(EqualPtr[int](newInt(1), newInt(2))) +} + +func TestComparePtr(t *testing.T) { + t.Parallel() + is := assert.New(t) + + newInt := func(v int) *int { return &v } + + is.Equal(0, ComparePtr[int](nil, nil)) + is.Equal(-1, ComparePtr[int](nil, newInt(1))) + is.Equal(1, ComparePtr[int](newInt(1), nil)) + is.Equal(0, ComparePtr[int](newInt(1), newInt(1))) + is.Equal(-1, ComparePtr[int](newInt(1), newInt(2))) + is.Equal(1, ComparePtr[int](newInt(2), newInt(1))) +}