diff --git a/README.md b/README.md index 253daa73..f18c6754 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ You can import `lo` using: import ( "github.com/samber/lo" lop "github.com/samber/lo/parallel" + lom "github.com/samber/lo/mutable" ) ``` @@ -344,6 +345,23 @@ even := lo.Filter([]int{1, 2, 3, 4}, func(x int, index int) bool { [[play](https://go.dev/play/p/Apjg3WeSi7K)] +Mutable: like `lo.Filter()`, but the slice is updated in place. + +```go +import lom "github.com/samber/lo/mutable" + +list := []int{1, 2, 3, 4} +newList := lom.Filter(list, func(x int) bool { + return x%2 == 0 +}) + +list +// []int{2, 4, 3, 4} + +newList +// []int{2, 4} +``` + ### Map Manipulates a slice of one type and transforms it into a slice of another type: @@ -370,6 +388,18 @@ lop.Map([]int64{1, 2, 3, 4}, func(x int64, _ int) string { // []string{"1", "2", "3", "4"} ``` +Mutable: like `lo.Map()`, but the slice is updated in place. + +```go +import lom "github.com/samber/lo/mutable" + +list := []int{1, 2, 3, 4} +lom.Map(list, func(x int) int { + return i*2 +}) +// []int{2, 4, 6, 8} +``` + ### UniqMap Manipulates a slice and transforms it to a slice of another type with unique values. diff --git a/mutable/slice.go b/mutable/slice.go index cd29cf55..969f3995 100644 --- a/mutable/slice.go +++ b/mutable/slice.go @@ -2,6 +2,54 @@ package mutable import "github.com/samber/lo/internal/rand" +// Filter is a generic function that modifies the input slice in-place to contain only the elements +// that satisfy the provided predicate function. The predicate function takes an element of the slice and its index, +// and should return true for elements that should be kept and false for elements that should be removed. +// The function returns the modified slice, which may be shorter than the original if some elements were removed. +// Note that the order of elements in the original slice is preserved in the output. +func Filter[T any, Slice ~[]T](collection Slice, predicate func(item T) bool) Slice { + j := 0 + for _, item := range collection { + if predicate(item) { + collection[j] = item + j++ + } + } + return collection[:j] +} + +// FilterI is a generic function that modifies the input slice in-place to contain only the elements +// that satisfy the provided predicate function. The predicate function takes an element of the slice and its index, +// and should return true for elements that should be kept and false for elements that should be removed. +// The function returns the modified slice, which may be shorter than the original if some elements were removed. +// Note that the order of elements in the original slice is preserved in the output. +func FilterI[T any, Slice ~[]T](collection Slice, predicate func(item T, index int) bool) Slice { + j := 0 + for i, item := range collection { + if predicate(item, i) { + collection[j] = item + j++ + } + } + return collection[:j] +} + +// Map is a generic function that modifies the input slice in-place to contain the result of applying the provided +// function to each element of the slice. The function returns the modified slice, which has the same length as the original. +func Map[T any, Slice ~[]T](collection Slice, fn func(item T) T) { + for i := range collection { + collection[i] = fn(collection[i]) + } +} + +// MapI is a generic function that modifies the input slice in-place to contain the result of applying the provided +// function to each element of the slice. The function returns the modified slice, which has the same length as the original. +func MapI[T any, Slice ~[]T](collection Slice, fn func(item T, index int) T) { + for i := range collection { + collection[i] = fn(collection[i], i) + } +} + // Shuffle returns an array of shuffled values. Uses the Fisher-Yates shuffle algorithm. // Play: https://go.dev/play/p/2xb3WdLjeSJ func Shuffle[T any, Slice ~[]T](collection Slice) { diff --git a/mutable/slice_example_test.go b/mutable/slice_example_test.go index 689fd8bd..6fb6a7ac 100644 --- a/mutable/slice_example_test.go +++ b/mutable/slice_example_test.go @@ -2,6 +2,54 @@ package mutable import "fmt" +func ExampleFilter() { + list := []int{1, 2, 3, 4} + + newList := Filter(list, func(nbr int) bool { + return nbr%2 == 0 + }) + + fmt.Printf("%v\n%v", list, newList) + // Output: + // [2 4 3 4] + // [2 4] +} + +func ExampleFilterI() { + list := []int{1, 2, 3, 4} + + newList := Filter(list, func(nbr int) bool { + return nbr%2 == 0 + }) + + fmt.Printf("%v\n%v", list, newList) + // Output: + // [2 4 3 4] + // [2 4] +} + +func ExampleMap() { + list := []int{1, 2, 3, 4} + + Map(list, func(nbr int) int { + return nbr * 2 + }) + + fmt.Printf("%v", list) + // Output: [2 4 6 8] +} + +func ExampleMapI() { + list := []int{1, 2, 3, 4} + + MapI(list, func(nbr int, index int) int { + return nbr * index + }) + + fmt.Printf("%v", list) + // Output: [0 2 6 12] +} + func ExampleShuffle() { list := []int{0, 1, 2, 3, 4, 5} diff --git a/mutable/slice_test.go b/mutable/slice_test.go index 7e10851b..7aac3927 100644 --- a/mutable/slice_test.go +++ b/mutable/slice_test.go @@ -6,6 +6,79 @@ import ( "github.com/stretchr/testify/assert" ) +func TestFilter(t *testing.T) { + t.Parallel() + is := assert.New(t) + + input1 := []int{1, 2, 3, 4} + r1 := Filter(input1, func(x int) bool { + return x%2 == 0 + }) + + is.Equal(input1, []int{2, 4, 3, 4}) + is.Equal(r1, []int{2, 4}) + + input2 := []string{"", "foo", "", "bar", ""} + r2 := Filter(input2, func(x string) bool { + return len(x) > 0 + }) + + is.Equal(input2, []string{"foo", "bar", "", "bar", ""}) + is.Equal(r2, []string{"foo", "bar"}) +} + +func TestFilterI(t *testing.T) { + t.Parallel() + is := assert.New(t) + + r1 := FilterI([]int{1, 2, 3, 4}, func(x int, i int) bool { + is.Equal(i, x-1) + return x%2 == 0 + }) + + is.Equal(r1, []int{2, 4}) +} + +func TestMap(t *testing.T) { + t.Parallel() + is := assert.New(t) + + list := []int{1, 2, 3, 4} + Map(list, func(x int) int { + return x * 2 + }) + is.Equal(len(list), 4) + is.Equal(list, []int{2, 4, 6, 8}) + + list = []int{1, 2, 3, 4} + Map(list, func(x int) int { + return x * 4 + }) + is.Equal(len(list), 4) + is.Equal(list, []int{4, 8, 12, 16}) +} + +func TestMapI(t *testing.T) { + t.Parallel() + is := assert.New(t) + + list := []int{1, 2, 3, 4} + MapI(list, func(x int, index int) int { + is.Equal(index, x-1) + return x * 2 + }) + is.Equal(len(list), 4) + is.Equal(list, []int{2, 4, 6, 8}) + + list = []int{1, 2, 3, 4} + MapI(list, func(x int, index int) int { + is.Equal(index, x-1) + return x * 4 + }) + is.Equal(len(list), 4) + is.Equal(list, []int{4, 8, 12, 16}) +} + func TestShuffle(t *testing.T) { t.Parallel() is := assert.New(t)