Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PartitionMatch method for slices #306

Merged
merged 1 commit into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 29 additions & 9 deletions generic/slices.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ func Contains[T any](s []T, eq EqualFunc[T], vals ...T) bool {
return true
}

// AnyMatch returns true if at least one item in a slice satisfies the provided predicate function.
// If no items satisfy the predicate or a slice is empty, it returns false.
// AnyMatch returns true if at least one element in a slice satisfies the provided predicate function.
// If no elements satisfy the predicate or a slice is empty, it returns false.
func AnyMatch[T any](s []T, p Predicate1[T]) bool {
for _, v := range s {
if p(v) {
Expand All @@ -73,7 +73,7 @@ func AnyMatch[T any](s []T, p Predicate1[T]) bool {
return false
}

// AllMatch returns true if all items in a slice satisfy the provided predicate function.
// AllMatch returns true if all elements in a slice satisfy the provided predicate function.
// If a slice is empty, it returns true.
func AllMatch[T any](s []T, p Predicate1[T]) bool {
for _, v := range s {
Expand All @@ -85,7 +85,7 @@ func AllMatch[T any](s []T, p Predicate1[T]) bool {
return true
}

// FirstMatch returns the first item in a slice that satisfies the given predicate.
// FirstMatch returns the first element in a slice that satisfies the given predicate.
// If no match is found, it returns the zero value of T and false.
func FirstMatch[T any](s []T, p Predicate1[T]) (T, bool) {
for _, v := range s {
Expand All @@ -98,18 +98,38 @@ func FirstMatch[T any](s []T, p Predicate1[T]) (T, bool) {
return zeroT, false
}

// SelectMatch selects a subset of items from a slice that satisfy the given predicate.
// It returns a new slice containing the matching items, of the same type as the original slice.
// SelectMatch selects a subset of elements from a slice that satisfy the given predicate.
// It returns a new slice containing the matching elements, of the same type as the original slice.
func SelectMatch[T any](s []T, p Predicate1[T]) []T {
ss := make([]T, 0)
matched := make([]T, 0)

for _, v := range s {
if p(v) {
ss = append(ss, v)
matched = append(matched, v)
}
}

return ss
return matched
}

// PartitionMatch partitions the elements in a slice
// into two separate slices based on the provided predicate.
// The first slice contains the elements that satisfy the predicate (matched elements),
// while the second slice contains those that do not satisfy the predicate (unmatched elements).
// Both slices are of the same type as the original slice.
func PartitionMatch[T any](s []T, p Predicate1[T]) ([]T, []T) {
matched := make([]T, 0)
unmatched := make([]T, 0)

for _, v := range s {
if p(v) {
matched = append(matched, v)
} else {
unmatched = append(unmatched, v)
}
}

return matched, unmatched
}

// Transform applies a given transformation function to each element of a slice,
Expand Down
45 changes: 36 additions & 9 deletions generic/slices_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,22 +269,49 @@ func TestFirstMatch(t *testing.T) {

func TestSelectMatch(t *testing.T) {
tests := []struct {
name string
s []string
p Predicate1[string]
expectedSelectMatch []string
name string
s []string
p Predicate1[string]
expectedSelected []string
}{
{
name: "OK",
s: []string{"Eagle", "Sparrow", "Owl", "Hummingbird", "Falcon", "Parrot", "Swan", "Seagull"},
p: func(s string) bool { return s[0] == 'S' },
expectedSelected: []string{"Sparrow", "Swan", "Seagull"},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
assert.Equal(t, tc.expectedSelected, SelectMatch(tc.s, tc.p))
})
}
}

func TestPartitionMatch(t *testing.T) {
tests := []struct {
name string
s []string
p Predicate1[string]
expectedMatched []string
expectedUnmatched []string
}{
{
name: "OK",
s: []string{"Eagle", "Sparrow", "Owl", "Hummingbird", "Falcon", "Parrot", "Swan", "Seagull"},
p: func(s string) bool { return s[0] == 'S' },
expectedSelectMatch: []string{"Sparrow", "Swan", "Seagull"},
name: "OK",
s: []string{"Eagle", "Sparrow", "Owl", "Hummingbird", "Falcon", "Parrot", "Swan", "Seagull"},
p: func(s string) bool { return s[0] == 'S' },
expectedMatched: []string{"Sparrow", "Swan", "Seagull"},
expectedUnmatched: []string{"Eagle", "Owl", "Hummingbird", "Falcon", "Parrot"},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
assert.Equal(t, tc.expectedSelectMatch, SelectMatch(tc.s, tc.p))
matched, unmatched := PartitionMatch(tc.s, tc.p)

assert.Equal(t, tc.expectedMatched, matched)
assert.Equal(t, tc.expectedUnmatched, unmatched)
})
}
}
Expand Down