feat: adding TrimXXX methods (#683)

This commit is contained in:
Samuel Berthe
2025-09-25 04:15:30 +02:00
committed by GitHub
parent 52d31f788f
commit ec86b574ed
4 changed files with 402 additions and 3 deletions

View File

@@ -126,7 +126,11 @@ Supported helpers for slices:
- [Cut](#Cut)
- [CutPrefix](#CutPrefix)
- [CutSuffix](#CutSuffix)
- [Trim](#Trim)
- [TrimLeft](#TrimLeft)
- [TrimPrefix](#TrimPrefix)
- [TrimRight](#TrimRight)
- [TrimSuffix](#TrimSuffix)
Supported helpers for maps:
@@ -1193,7 +1197,7 @@ result = lo.Splice([]string{"a", "b"}, 42, "1", "2")
// []string{"a", "b", "1", "2"}
```
[[play](https://go.dev/play/p/wiG6XyBBu49)]
[[play](https://go.dev/play/p/G5_GhkeSUBA)]
### Cut
@@ -1216,6 +1220,8 @@ result = lo.Cut([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b"})
// result: true
```
[[play](https://go.dev/play/p/GiL3qhpIP3f)]
### CutPrefix
Returns collection without the provided leading prefix []T and reports whether it found the prefix. If s doesn't start with prefix, CutPrefix returns collection, false. If prefix is the empty []T, CutPrefix returns collection, true.
@@ -1234,6 +1240,8 @@ result = lo.CutPrefix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{})
// result: true
```
[[play](https://go.dev/play/p/7Plak4a1ICl)]
### CutSuffix
Returns collection without the provided ending suffix []T and reports whether it found the suffix. If it doesn't end with suffix, CutSuffix returns collection, false. If suffix is the empty []T, CutSuffix returns collection, true.
@@ -1252,6 +1260,78 @@ actualLeft, result = lo.CutSuffix([]string{"a", "b", "c", "d", "e", "f", "g"}, [
// result: true
```
[[play](https://go.dev/play/p/7FKfBFvPTaT)]
### Trim
Removes all the leading and trailing cutset from the collection.
```go
result := lo.Trim([]int{0, 1, 2, 0, 3, 0}, []int{1, 0})
// []int{2, 0, 3}
result := lo.Trim([]string{"hello", "world", " "}, []string{" ", ""})
// []string{"hello", "world"}
```
[[play](https://go.dev/play/p/1an9mxLdRG5)]
### TrimLeft
Removes all the leading cutset from the collection.
```go
result := lo.TrimLeft([]int{0, 1, 2, 0, 3, 0}, []int{1, 0})
// []int{2, 0, 3, 0}
result := lo.TrimLeft([]string{"hello", "world", " "}, []string{" ", ""})
// []string{"hello", "world", " "}
```
[[play](https://go.dev/play/p/74aqfAYLmyi)]
### TrimPrefix
Removes all the leading prefix from the collection.
```go
result := lo.TrimPrefix([]int{1, 2, 1, 2, 3, 1, 2, 4}, []int{1, 2})
// []int{3, 1, 2, 4}
result := lo.TrimPrefix([]string{"hello", "world", "hello", "test"}, []string{"hello"})
// []string{"world", "hello", "test"}
```
[[play](https://go.dev/play/p/SHO6X-YegPg)]
### TrimRight
Removes all the trailing cutset from the collection.
```go
result := lo.TrimRight([]int{0, 1, 2, 0, 3, 0}, []int{0, 3})
// []int{0, 1, 2}
result := lo.TrimRight([]string{"hello", "world", " "}, []string{" ", ""})
// []string{"hello", "world", ""}
```
[[play](https://go.dev/play/p/MRpAfR6sf0g)]
### TrimSuffix
Removes all the trailing suffix from the collection.
```go
result := lo.TrimSuffix([]int{1, 2, 3, 1, 2, 4, 2, 4, 2, 4}, []int{2, 4})
// []int{1, 2, 3, 1}
result := lo.TrimSuffix([]string{"hello", "world", "hello", "test"}, []string{"test"})
// []string{"hello", "world", "hello"}
```
[[play](https://go.dev/play/p/IjEUrV0iofq)]
### Keys
Creates a slice of the map keys.

View File

@@ -722,7 +722,6 @@ func IsSorted[T constraints.Ordered](collection []T) bool {
}
// IsSortedByKey checks if a slice is sorted by iteratee.
// Play: https://go.dev/play/p/wiG6XyBBu49
func IsSortedByKey[T any, K constraints.Ordered](collection []T, iteratee func(item T) K) bool {
size := len(collection)
@@ -763,6 +762,7 @@ func Splice[T any, Slice ~[]T](collection Slice, i int, elements ...T) Slice {
// Cut slices collection around the first instance of separator, returning the part of collection
// before and after separator. The found result reports whether separator appears in collection.
// If separator does not appear in s, cut returns collection, empty slice of []T, false.
// Play: https://go.dev/play/p/GiL3qhpIP3f
func Cut[T comparable, Slice ~[]T](collection Slice, separator Slice) (before Slice, after Slice, found bool) {
if len(separator) == 0 {
return make(Slice, 0), collection, true
@@ -788,6 +788,7 @@ func Cut[T comparable, Slice ~[]T](collection Slice, separator Slice) (before Sl
// and reports whether it found the prefix.
// If s doesn't start with prefix, CutPrefix returns collection, false.
// If prefix is the empty []T, CutPrefix returns collection, true.
// Play: https://go.dev/play/p/7Plak4a1ICl
func CutPrefix[T comparable, Slice ~[]T](collection Slice, separator Slice) (after Slice, found bool) {
if len(separator) == 0 {
return collection, true
@@ -808,6 +809,7 @@ func CutPrefix[T comparable, Slice ~[]T](collection Slice, separator Slice) (aft
// CutSuffix returns collection without the provided ending suffix []T and reports
// whether it found the suffix. If s doesn't end with suffix, CutSuffix returns collection, false.
// If suffix is the empty []T, CutSuffix returns collection, true.
// Play: https://go.dev/play/p/7FKfBFvPTaT
func CutSuffix[T comparable, Slice ~[]T](collection Slice, separator Slice) (before Slice, found bool) {
if len(separator) == 0 {
return collection, true
@@ -825,3 +827,61 @@ func CutSuffix[T comparable, Slice ~[]T](collection Slice, separator Slice) (bef
return collection[:start], true
}
// Trim removes all the leading and trailing cutset from the collection.
// Play: https://go.dev/play/p/1an9mxLdRG5
func Trim[T comparable, Slice ~[]T](collection Slice, cutset Slice) Slice {
return TrimLeft(TrimRight(collection, cutset), cutset)
}
// TrimLeft removes all the leading cutset from the collection.
// Play: https://go.dev/play/p/74aqfAYLmyi
func TrimLeft[T comparable, Slice ~[]T](collection Slice, cutset Slice) Slice {
set := Keyify(cutset)
return DropWhile(collection, func(item T) bool {
_, ok := set[item]
return ok
})
}
// TrimPrefix removes all the leading prefix from the collection.
// Play: https://go.dev/play/p/SHO6X-YegPg
func TrimPrefix[T comparable, Slice ~[]T](collection Slice, prefix Slice) Slice {
if len(prefix) == 0 {
return collection
}
for {
if !HasPrefix(collection, prefix) {
return collection
}
collection = collection[len(prefix):]
}
}
// TrimRight removes all the trailing cutset from the collection.
// Play: https://go.dev/play/p/MRpAfR6sf0g
func TrimRight[T comparable, Slice ~[]T](collection Slice, cutset Slice) Slice {
set := Keyify(cutset)
return DropRightWhile(collection, func(item T) bool {
_, ok := set[item]
return ok
})
}
// TrimSuffix removes all the trailing suffix from the collection.
// Play: https://go.dev/play/p/IjEUrV0iofq
func TrimSuffix[T comparable, Slice ~[]T](collection Slice, suffix Slice) Slice {
if len(suffix) == 0 {
return collection
}
for {
if !HasSuffix(collection, suffix) {
return collection
}
collection = collection[:len(collection)-len(suffix)]
}
}

View File

@@ -539,3 +539,176 @@ func ExampleIsSortedByKey() {
// Output: true
}
func ExampleCut() {
collection := []string{"a", "b", "c", "d", "e", "f", "g"}
// Test with valid separator
before, after, found := Cut(collection, []string{"b", "c", "d"})
fmt.Printf("Before: %v, After: %v, Found: %t\n", before, after, found)
// Test with separator not found
before2, after2, found2 := Cut(collection, []string{"z"})
fmt.Printf("Before: %v, After: %v, Found: %t\n", before2, after2, found2)
// Test with separator at beginning
before3, after3, found3 := Cut(collection, []string{"a", "b"})
fmt.Printf("Before: %v, After: %v, Found: %t\n", before3, after3, found3)
// Output:
// Before: [a], After: [e f g], Found: true
// Before: [a b c d e f g], After: [], Found: false
// Before: [], After: [c d e f g], Found: true
}
func ExampleCutPrefix() {
collection := []string{"a", "b", "c", "d", "e", "f", "g"}
// Test with valid prefix
after, found := CutPrefix(collection, []string{"a", "b", "c"})
fmt.Printf("After: %v, Found: %t\n", after, found)
// Test with prefix not found
after2, found2 := CutPrefix(collection, []string{"b"})
fmt.Printf("After: %v, Found: %t\n", after2, found2)
// Test with empty prefix
after3, found3 := CutPrefix(collection, []string{})
fmt.Printf("After: %v, Found: %t\n", after3, found3)
// Output:
// After: [d e f g], Found: true
// After: [a b c d e f g], Found: false
// After: [a b c d e f g], Found: true
}
func ExampleCutSuffix() {
collection := []string{"a", "b", "c", "d", "e", "f", "g"}
// Test with valid suffix
before, found := CutSuffix(collection, []string{"f", "g"})
fmt.Printf("Before: %v, Found: %t\n", before, found)
// Test with suffix not found
before2, found2 := CutSuffix(collection, []string{"b"})
fmt.Printf("Before: %v, Found: %t\n", before2, found2)
// Test with empty suffix
before3, found3 := CutSuffix(collection, []string{})
fmt.Printf("Before: %v, Found: %t\n", before3, found3)
// Output:
// Before: [a b c d e], Found: true
// Before: [a b c d e f g], Found: false
// Before: [a b c d e f g], Found: true
}
func ExampleTrim() {
collection := []int{0, 1, 2, 0, 3, 0}
// Test with valid cutset
result := Trim(collection, []int{0})
fmt.Printf("Trim with cutset {0}: %v\n", result)
// Test with string collection
words := []string{" hello ", "world", " "}
result2 := Trim(words, []string{" "})
fmt.Printf("Trim with string cutset: %v\n", result2)
// Test with no cutset elements
result3 := Trim(collection, []int{5})
fmt.Printf("Trim with cutset {5} (not present): %v\n", result3)
// Output:
// Trim with cutset {0}: [1 2 0 3]
// Trim with string cutset: [ hello world ]
// Trim with cutset {5} (not present): [0 1 2 0 3 0]
}
func ExampleTrimLeft() {
collection := []int{0, 1, 2, 0, 3, 0}
// Test with valid cutset
result := TrimLeft(collection, []int{0})
fmt.Printf("TrimLeft with cutset {0}: %v\n", result)
// Test with string collection
words := []string{" hello ", "world", " "}
result2 := TrimLeft(words, []string{" "})
fmt.Printf("TrimLeft with string cutset: %v\n", result2)
// Test with no cutset elements
result3 := TrimLeft(collection, []int{5})
fmt.Printf("TrimLeft with cutset {5} (not present): %v\n", result3)
// Output:
// TrimLeft with cutset {0}: [1 2 0 3 0]
// TrimLeft with string cutset: [ hello world ]
// TrimLeft with cutset {5} (not present): [0 1 2 0 3 0]
}
func ExampleTrimPrefix() {
collection := []int{1, 2, 1, 2, 3}
// Test with valid prefix
result := TrimPrefix(collection, []int{1, 2})
fmt.Printf("TrimPrefix with prefix {1,2}: %v\n", result)
// Test with string collection
words := []string{"hello", "hello", "world"}
result2 := TrimPrefix(words, []string{"hello"})
fmt.Printf("TrimPrefix with string prefix: %v\n", result2)
// Test with prefix not present
result3 := TrimPrefix(collection, []int{5, 6})
fmt.Printf("TrimPrefix with prefix {5,6} (not present): %v\n", result3)
// Output:
// TrimPrefix with prefix {1,2}: [3]
// TrimPrefix with string prefix: [world]
// TrimPrefix with prefix {5,6} (not present): [1 2 1 2 3]
}
func ExampleTrimRight() {
collection := []int{0, 1, 2, 0, 3, 0}
// Test with valid cutset
result := TrimRight(collection, []int{0})
fmt.Printf("TrimRight with cutset {0}: %v\n", result)
// Test with string collection
words := []string{" hello ", "world", " "}
result2 := TrimRight(words, []string{" "})
fmt.Printf("TrimRight with string cutset: %v\n", result2)
// Test with no cutset elements
result3 := TrimRight(collection, []int{5})
fmt.Printf("TrimRight with cutset {5} (not present): %v\n", result3)
// Output:
// TrimRight with cutset {0}: [0 1 2 0 3]
// TrimRight with string cutset: [ hello world ]
// TrimRight with cutset {5} (not present): [0 1 2 0 3 0]
}
func ExampleTrimSuffix() {
collection := []int{1, 2, 1, 2, 3}
// Test with valid suffix
result := TrimSuffix(collection, []int{1, 2})
fmt.Printf("TrimSuffix with suffix {1,2}: %v\n", result)
// Test with string collection
words := []string{"hello", "world", "test"}
result2 := TrimSuffix(words, []string{"test"})
fmt.Printf("TrimSuffix with string suffix: %v\n", result2)
// Test with suffix not present
result3 := TrimSuffix(collection, []int{5, 6})
fmt.Printf("TrimSuffix with suffix {5,6} (not present): %v\n", result3)
// Output:
// TrimSuffix with suffix {1,2}: [1 2 1 2 3]
// TrimSuffix with string suffix: [hello world]
// TrimSuffix with suffix {5,6} (not present): [1 2 1 2 3]
}

View File

@@ -1288,3 +1288,89 @@ func TestCutSuffix(t *testing.T) {
is.True(result)
is.Equal([]string{"a", "a", "b"}, actualAfterS)
}
func TestTrim(t *testing.T) {
t.Parallel()
is := assert.New(t)
actual := Trim([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b"})
is.Equal([]string{"c", "d", "e", "f", "g"}, actual)
actual = Trim([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"g", "f"})
is.Equal([]string{"a", "b", "c", "d", "e"}, actual)
actual = Trim([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g"})
is.Equal([]string{}, actual)
actual = Trim([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g", "h"})
is.Equal([]string{}, actual)
actual = Trim([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
}
func TestTrimLeft(t *testing.T) {
t.Parallel()
is := assert.New(t)
actual := TrimLeft([]string{"a", "a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b"})
is.Equal([]string{"c", "d", "e", "f", "g"}, actual)
actual = TrimLeft([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"b", "a"})
is.Equal([]string{"c", "d", "e", "f", "g"}, actual)
actual = TrimLeft([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"g", "f"})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
actual = TrimLeft([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g"})
is.Equal([]string{}, actual)
actual = TrimLeft([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g", "h"})
is.Equal([]string{}, actual)
actual = TrimLeft([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
}
func TestTrimPrefix(t *testing.T) {
t.Parallel()
is := assert.New(t)
actual := TrimPrefix([]string{"a", "b", "a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b"})
is.Equal([]string{"c", "d", "e", "f", "g"}, actual)
actual = TrimPrefix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"b", "a"})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
actual = TrimPrefix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"g", "f"})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
actual = TrimPrefix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g"})
is.Equal([]string{}, actual)
actual = TrimPrefix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g", "h"})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
actual = TrimPrefix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
}
func TestTrimRight(t *testing.T) {
t.Parallel()
is := assert.New(t)
actual := TrimRight([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b"})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
actual = TrimRight([]string{"a", "b", "c", "d", "e", "f", "g", "g"}, []string{"g", "f"})
is.Equal([]string{"a", "b", "c", "d", "e"}, actual)
actual = TrimRight([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g"})
is.Equal([]string{}, actual)
actual = TrimRight([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g", "h"})
is.Equal([]string{}, actual)
actual = TrimRight([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
}
func TestTrimSuffix(t *testing.T) {
t.Parallel()
is := assert.New(t)
actual := TrimSuffix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b"})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
actual = TrimSuffix([]string{"a", "b", "c", "d", "e", "f", "g", "f", "g"}, []string{"f", "g"})
is.Equal([]string{"a", "b", "c", "d", "e"}, actual)
actual = TrimSuffix([]string{"a", "b", "c", "d", "e", "f", "g", "f", "g"}, []string{"g", "f"})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g", "f", "g"}, actual)
actual = TrimSuffix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g"})
is.Equal([]string{}, actual)
actual = TrimSuffix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g", "h"})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
actual = TrimSuffix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
}