diff --git a/README.md b/README.md index 0f09c7bc..f9ccde38 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,7 @@ Supported helpers for slices: - [Compact](#compact) - [IsSorted](#issorted) - [IsSortedByKey](#issortedbykey) +- [Splice](#Splice) Supported helpers for maps: @@ -980,6 +981,25 @@ slice := lo.IsSortedByKey([]string{"a", "bb", "ccc"}, func(s string) int { [[play](https://go.dev/play/p/wiG6XyBBu49)] +### Splice + +Splice inserts multiple elements at index i. A negative index counts back from the end of the slice. The helper is protected against overflow errors. + +```go +result := lo.Splice([]string{"a", "b"}, 1, "1", "2") +// []string{"a", "1", "2", "b"} + +// negative +result = lo.Splice([]string{"a", "b"}, -1, "1", "2") +// []string{"a", "1", "2", "b"} + +// overflow +result = lo.Splice([]string{"a", "b"}, 42, "1", "2") +// []string{"a", "b", "1", "2"} +``` + +[[play](https://go.dev/play/p/G5_GhkeSUBA)] + ### Keys Creates an array of the map keys. diff --git a/slice.go b/slice.go index 7ef820ad..5c794d79 100644 --- a/slice.go +++ b/slice.go @@ -625,3 +625,27 @@ func IsSortedByKey[T any, K constraints.Ordered](collection []T, iteratee func(i return true } + +// Splice inserts multiple elements at index i. A negative index counts back +// from the end of the slice. The helper is protected against overflow errors. +// Play: https://go.dev/play/p/G5_GhkeSUBA +func Splice[T any](collection []T, i int, elements ...T) []T { + sizeCollection := len(collection) + sizeElements := len(elements) + output := make([]T, 0, sizeCollection+sizeElements) // preallocate memory for the output slice + + if sizeElements == 0 { + return append(output, collection...) // simple copy + } else if i > sizeCollection { + // positive overflow + return append(append(output, collection...), elements...) + } else if i < -sizeCollection { + // negative overflow + return append(append(output, elements...), collection...) + } else if i < 0 { + // backward + i = sizeCollection + i + } + + return append(append(append(output, collection[:i]...), elements...), collection[i:]...) +} diff --git a/slice_test.go b/slice_test.go index e7e717fd..cd3ee829 100644 --- a/slice_test.go +++ b/slice_test.go @@ -801,3 +801,47 @@ func TestIsSortedByKey(t *testing.T) { return ret })) } + +func TestSplice(t *testing.T) { + t.Parallel() + is := assert.New(t) + + sample := []string{"a", "b", "c", "d", "e", "f", "g"} + + // normal case + results := Splice(sample, 1, "1", "2") + is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, sample) + is.Equal([]string{"a", "1", "2", "b", "c", "d", "e", "f", "g"}, results) + + // check there is no side effect + results = Splice(sample, 1) + results[0] = "b" + is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, sample) + + // positive overflow + results = Splice(sample, 42, "1", "2") + is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, sample) + is.Equal(results, []string{"a", "b", "c", "d", "e", "f", "g", "1", "2"}) + + // negative overflow + results = Splice(sample, -42, "1", "2") + is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, sample) + is.Equal(results, []string{"1", "2", "a", "b", "c", "d", "e", "f", "g"}) + + // backard + results = Splice(sample, -2, "1", "2") + is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, sample) + is.Equal(results, []string{"a", "b", "c", "d", "e", "1", "2", "f", "g"}) + + results = Splice(sample, -7, "1", "2") + is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, sample) + is.Equal(results, []string{"1", "2", "a", "b", "c", "d", "e", "f", "g"}) + + // other + is.Equal([]string{"1", "2"}, Splice([]string{}, 0, "1", "2")) + is.Equal([]string{"1", "2"}, Splice([]string{}, 1, "1", "2")) + is.Equal([]string{"1", "2"}, Splice([]string{}, -1, "1", "2")) + is.Equal([]string{"1", "2", "0"}, Splice([]string{"0"}, 0, "1", "2")) + is.Equal([]string{"0", "1", "2"}, Splice([]string{"0"}, 1, "1", "2")) + is.Equal([]string{"1", "2", "0"}, Splice([]string{"0"}, -1, "1", "2")) +}