From d22a2a264d97eff0770d1ae67cdc77e2ad3dd02e Mon Sep 17 00:00:00 2001 From: Samuel Berthe Date: Sat, 30 Apr 2022 23:23:09 +0200 Subject: [PATCH] feat: adding Replace and ReplaceAll --- CHANGELOG.md | 4 ++ README.md | 70 +++++++++++++++++++++++++++++++++- slice.go | 66 ++++++++++++++++++++++++++++++++ slice_test.go | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 240 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e163230c..66d6b37d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ Adding: - lo.RepeatBy +- lo.Substring +- lo.Subset +- lo.Replace +- lo.ReplaceAll ## 1.18.0 (2022-04-28) diff --git a/README.md b/README.md index 447e2a2a..fd42654d 100644 --- a/README.md +++ b/README.md @@ -563,7 +563,7 @@ odd := lo.Reject[int]([]int{1, 2, 3, 4}, func(x int, _ int) bool { Counts the number of elements in the collection that compare equal to value. ```go -count := Count[int]([]int{1, 5, 1}, 1) +count := lo.Count[int]([]int{1, 5, 1}, 1) // 2 ``` @@ -572,12 +572,78 @@ count := Count[int]([]int{1, 5, 1}, 1) Counts the number of elements in the collection for which predicate is true. ```go -count := CountBy[int]([]int{1, 5, 1}, func(i int) bool { +count := lo.CountBy[int]([]int{1, 5, 1}, func(i int) bool { return i < 4 }) // 2 ``` +### Substring + +Return part of a string. + +```go +sub := lo.Substring("hello", 2, 3) +// "llo" + +sub := lo.Substring("hello", -4, 3) +// "ell" + +sub := lo.Substring("hello", -2, math.MaxUint) +// "lo" +``` + +### Subset + +Return part of a slice. + +```go +in := []int{0, 1, 2, 3, 4} + +sub := lo.Subset(in, 2, 3) +// []int{2, 3, 4} + +sub := lo.Subset(in, -4, 3) +// []int{1, 2, 3} + +sub := lo.Subset(in, -2, math.MaxUint) +// []int{3, 4} +``` + +### Replace + +Returns a copy of the slice with the first n non-overlapping instances of old replaced by new. + +```go +in := []int{0, 1, 0, 1, 2, 3, 0} + +slice := lo.Replace(in, 0, 42, 1) +// []int{42, 1, 0, 1, 2, 3, 0} + +slice := lo.Replace(in, -1, 42, 1) +// []int{0, 1, 0, 1, 2, 3, 0} + +slice := lo.Replace(in, 0, 42, 2) +// []int{42, 1, 42, 1, 2, 3, 0} + +slice := lo.Replace(in, 0, 42, -1) +// []int{42, 1, 42, 1, 2, 3, 42} +``` + +### ReplaceAll + +Returns a copy of the slice with all non-overlapping instances of old replaced by new. + +```go +in := []int{0, 1, 0, 1, 2, 3, 0} + +slice := lo.ReplaceAll(in, 0, 42) +// []int{42, 1, 42, 1, 2, 3, 42} + +slice := lo.ReplaceAll(in, -1, 42) +// []int{0, 1, 0, 1, 2, 3, 0} +``` + ### Keys Creates an array of the map keys. diff --git a/slice.go b/slice.go index 1a7a5827..8b50e3b5 100644 --- a/slice.go +++ b/slice.go @@ -362,3 +362,69 @@ func CountBy[T any](collection []T, predicate func(T) bool) (count int) { return count } + +// Substring return part of a string. +func Substring[T ~string](str T, offset int, length uint) T { + size := len(str) + + if offset < 0 { + offset = size + offset + if offset < 0 { + offset = 0 + } + } + + if offset > size { + return Empty[T]() + } + + if length > uint(size)-uint(offset) { + length = uint(size - offset) + } + + return str[offset : offset+int(length)] +} + +// Subset return part of a slice. +func Subset[T any](collection []T, offset int, length uint) []T { + size := len(collection) + + if offset < 0 { + offset = size + offset + if offset < 0 { + offset = 0 + } + } + + if offset > size { + return []T{} + } + + if length > uint(size)-uint(offset) { + length = uint(size - offset) + } + + return collection[offset : offset+int(length)] +} + +// Replace returns a copy of the slice with the first n non-overlapping instances of old replaced by new. +func Replace[T comparable](collection []T, old T, new T, n int) []T { + size := len(collection) + result := make([]T, 0, size) + + for _, item := range collection { + if item == old && n != 0 { + result = append(result, new) + n-- + } else { + result = append(result, item) + } + } + + return result +} + +// ReplaceAll returns a copy of the slice with all non-overlapping instances of old replaced by new. +func ReplaceAll[T comparable](collection []T, old T, new T) []T { + return Replace[T](collection, old, new, -1) +} diff --git a/slice_test.go b/slice_test.go index d4617aa7..c709f678 100644 --- a/slice_test.go +++ b/slice_test.go @@ -383,3 +383,105 @@ func TestCountBy(t *testing.T) { is.Equal(count2, 0) is.Equal(count3, 0) } + +func TestSubstring(t *testing.T) { + is := assert.New(t) + + str1 := Substring("hello", 0, 0) + str2 := Substring("hello", 10, 2) + str3 := Substring("hello", -10, 2) + str4 := Substring("hello", 0, 10) + str5 := Substring("hello", 0, 2) + str6 := Substring("hello", 2, 2) + str7 := Substring("hello", 2, 5) + str8 := Substring("hello", 2, 3) + str9 := Substring("hello", 2, 4) + str10 := Substring("hello", -2, 4) + str11 := Substring("hello", -4, 1) + str12 := Substring("hello", -4, math.MaxUint) + + is.Equal("", str1) + is.Equal("", str2) + is.Equal("he", str3) + is.Equal("hello", str4) + is.Equal("he", str5) + is.Equal("ll", str6) + is.Equal("llo", str7) + is.Equal("llo", str8) + is.Equal("llo", str9) + is.Equal("lo", str10) + is.Equal("e", str11) + is.Equal("ello", str12) +} + +func TestSubset(t *testing.T) { + is := assert.New(t) + + in := []int{0, 1, 2, 3, 4} + + out1 := Subset(in, 0, 0) + out2 := Subset(in, 10, 2) + out3 := Subset(in, -10, 2) + out4 := Subset(in, 0, 10) + out5 := Subset(in, 0, 2) + out6 := Subset(in, 2, 2) + out7 := Subset(in, 2, 5) + out8 := Subset(in, 2, 3) + out9 := Subset(in, 2, 4) + out10 := Subset(in, -2, 4) + out11 := Subset(in, -4, 1) + out12 := Subset(in, -4, math.MaxUint) + + is.Equal([]int{}, out1) + is.Equal([]int{}, out2) + is.Equal([]int{0, 1}, out3) + is.Equal([]int{0, 1, 2, 3, 4}, out4) + is.Equal([]int{0, 1}, out5) + is.Equal([]int{2, 3}, out6) + is.Equal([]int{2, 3, 4}, out7) + is.Equal([]int{2, 3, 4}, out8) + is.Equal([]int{2, 3, 4}, out9) + is.Equal([]int{3, 4}, out10) + is.Equal([]int{1}, out11) + is.Equal([]int{1, 2, 3, 4}, out12) +} + +func TestReplace(t *testing.T) { + is := assert.New(t) + + in := []int{0, 1, 0, 1, 2, 3, 0} + + out1 := Replace(in, 0, 42, 2) + out2 := Replace(in, 0, 42, 1) + out3 := Replace(in, 0, 42, 0) + out4 := Replace(in, 0, 42, -1) + out5 := Replace(in, 0, 42, -1) + out6 := Replace(in, -1, 42, 2) + out7 := Replace(in, -1, 42, 1) + out8 := Replace(in, -1, 42, 0) + out9 := Replace(in, -1, 42, -1) + out10 := Replace(in, -1, 42, -1) + + is.Equal([]int{42, 1, 42, 1, 2, 3, 0}, out1) + is.Equal([]int{42, 1, 0, 1, 2, 3, 0}, out2) + is.Equal([]int{0, 1, 0, 1, 2, 3, 0}, out3) + is.Equal([]int{42, 1, 42, 1, 2, 3, 42}, out4) + is.Equal([]int{42, 1, 42, 1, 2, 3, 42}, out5) + is.Equal([]int{0, 1, 0, 1, 2, 3, 0}, out6) + is.Equal([]int{0, 1, 0, 1, 2, 3, 0}, out7) + is.Equal([]int{0, 1, 0, 1, 2, 3, 0}, out8) + is.Equal([]int{0, 1, 0, 1, 2, 3, 0}, out9) + is.Equal([]int{0, 1, 0, 1, 2, 3, 0}, out10) +} + +func TestReplaceAll(t *testing.T) { + is := assert.New(t) + + in := []int{0, 1, 0, 1, 2, 3, 0} + + out1 := ReplaceAll(in, 0, 42) + out2 := ReplaceAll(in, -1, 42) + + is.Equal([]int{42, 1, 42, 1, 2, 3, 42}, out1) + is.Equal([]int{0, 1, 0, 1, 2, 3, 0}, out2) +}