diff --git a/README.md b/README.md index 648824a..f03e2bd 100644 --- a/README.md +++ b/README.md @@ -431,6 +431,19 @@ for number := range itx.FromChannel(items).Exclude(filter.IsZero) { > In order to prevent a deadlock, the channel must be closed before attemping to stop the iterator > when it's used in a pull style. See [iter.Pull](https://pkg.go.dev/iter#Pull). +### Compact + +Compact yields all values from a delegate iterator that are not zero values. It is functionally +equivalent to `it.Filter(delegate, filter.IsZero)`. + +```go +words := it.Compact(slices.Values([]string{"foo", "", "bar", "", ""})) +``` + + +> [!NOTE] +> The `itx` package does not contain `Compact` due to limitations with Go's type system. + ### Cycle Cycle yields all values from an iterator before returning to the beginning and yielding all values diff --git a/it/compact.go b/it/compact.go new file mode 100644 index 0000000..6a24c54 --- /dev/null +++ b/it/compact.go @@ -0,0 +1,19 @@ +package it + +import "iter" + +// Compact yields all values from a delegate iterator that are not zero values. +func Compact[V comparable](delegate func(func(V) bool)) iter.Seq[V] { + return func(yield func(V) bool) { + for value := range delegate { + var zero V + if zero == value { + continue + } + + if !yield(value) { + return + } + } + } +} diff --git a/it/compact_test.go b/it/compact_test.go new file mode 100644 index 0000000..df5f51a --- /dev/null +++ b/it/compact_test.go @@ -0,0 +1,47 @@ +package it_test + +import ( + "fmt" + "slices" + "testing" + + "github.com/BooleanCat/go-functional/v2/internal/assert" + "github.com/BooleanCat/go-functional/v2/it" +) + +func ExampleCompact() { + words := slices.Values([]string{"", "foo", "", "", "bar", ""}) + fmt.Println(slices.Collect(it.Compact(words))) + // Output: [foo bar] +} + +func TestCompactEmpty(t *testing.T) { + t.Parallel() + + words := slices.Collect(it.Compact(it.Exhausted[string]())) + assert.Empty[string](t, words) +} + +func TestCompactOnlyEmpty(t *testing.T) { + t.Parallel() + + words := slices.Values([]string{"", "", "", ""}) + assert.Empty[string](t, slices.Collect(it.Compact(words))) +} + +func TestCompactOnlyNotEmpty(t *testing.T) { + t.Parallel() + + words := slices.Values([]string{"foo", "bar"}) + assert.SliceEqual(t, slices.Collect(it.Compact(words)), []string{"foo", "bar"}) +} + +func TestCompactYieldFalse(t *testing.T) { + t.Parallel() + + words := it.Compact(slices.Values([]string{"foo", "bar"})) + + words(func(string) bool { + return false + }) +}