From a70778ac822e98e44afbb631785d23e237d0a51c Mon Sep 17 00:00:00 2001 From: Aiden <12055114+aidenwallis@users.noreply.github.com> Date: Thu, 6 Apr 2023 13:23:50 +0100 Subject: [PATCH] Add slice mutation utils (#24) --- utils/insert_in_slice.go | 11 ++++++++ utils/insert_in_slice_test.go | 50 +++++++++++++++++++++++++++++++++ utils/move_in_slice.go | 12 ++++++++ utils/move_in_slice_test.go | 48 +++++++++++++++++++++++++++++++ utils/remove_from_slice.go | 9 ++++++ utils/remove_from_slice_test.go | 44 +++++++++++++++++++++++++++++ 6 files changed, 174 insertions(+) create mode 100644 utils/insert_in_slice.go create mode 100644 utils/insert_in_slice_test.go create mode 100644 utils/move_in_slice.go create mode 100644 utils/move_in_slice_test.go create mode 100644 utils/remove_from_slice.go create mode 100644 utils/remove_from_slice_test.go diff --git a/utils/insert_in_slice.go b/utils/insert_in_slice.go new file mode 100644 index 0000000..741429b --- /dev/null +++ b/utils/insert_in_slice.go @@ -0,0 +1,11 @@ +package utils + +// InsertInSlice inserts a value into a slice at a given position +// +// If the position gives is out of bounds then it resorts to inserting to the end of the slice +func InsertInSlice[T any](in []T, valueToAdd T, index int) []T { + if index > len(in) { + index = len(in) + } + return append(in[:index], append([]T{valueToAdd}, in[index:]...)...) +} diff --git a/utils/insert_in_slice_test.go b/utils/insert_in_slice_test.go new file mode 100644 index 0000000..47b5b71 --- /dev/null +++ b/utils/insert_in_slice_test.go @@ -0,0 +1,50 @@ +package utils_test + +import ( + "log" + "testing" + + "github.com/aidenwallis/go-utils/internal/assert" + "github.com/aidenwallis/go-utils/utils" +) + +func TestInsertInSlice(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in []int + out []int + valueToAdd int + index int + }{ + "normal operation": { + in: []int{0, 1, 2, 3, 4}, + out: []int{0, 1, 2000, 2, 3, 4}, + valueToAdd: 2000, + index: 2, + }, + + "out of bounds": { + in: []int{0, 1, 2, 3}, + out: []int{0, 1, 2, 3, 2000}, + valueToAdd: 2000, + index: 2000, + }, + } + + for name, testCase := range testCases { + testCase := testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + result := utils.InsertInSlice(testCase.in, testCase.valueToAdd, testCase.index) + log.Println(result) + + assert.Equal(t, len(testCase.out), len(result)) + for i, v := range result { + assert.Equal(t, testCase.out[i], v) + } + }) + } +} diff --git a/utils/move_in_slice.go b/utils/move_in_slice.go new file mode 100644 index 0000000..e24f9a3 --- /dev/null +++ b/utils/move_in_slice.go @@ -0,0 +1,12 @@ +package utils + +// MoveInSlice moves a value within the slice to a specified new position +// +// If either the old or new position is out of bounds, the existing slice contents is returned. +func MoveInSlice[T any](in []T, oldPosition, newPosition int) []T { + if oldPosition >= len(in) { + return in + } + value := in[oldPosition] + return InsertInSlice(RemoveFromSlice(in, oldPosition), value, newPosition) +} diff --git a/utils/move_in_slice_test.go b/utils/move_in_slice_test.go new file mode 100644 index 0000000..46e7cae --- /dev/null +++ b/utils/move_in_slice_test.go @@ -0,0 +1,48 @@ +package utils_test + +import ( + "testing" + + "github.com/aidenwallis/go-utils/internal/assert" + "github.com/aidenwallis/go-utils/utils" +) + +func TestMoveInSlice(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in []int + out []int + oldPosition int + newPosition int + }{ + "normal operation": { + in: []int{0, 1, 2, 3, 4}, + out: []int{0, 3, 1, 2, 4}, + oldPosition: 3, + newPosition: 1, + }, + + "out of bounds in old position": { + in: []int{0, 1, 2, 3, 4}, + out: []int{0, 1, 2, 3, 4}, + oldPosition: 5, + newPosition: 0, + }, + } + + for name, testCase := range testCases { + testCase := testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + result := utils.MoveInSlice(testCase.in, testCase.oldPosition, testCase.newPosition) + + assert.Equal(t, len(testCase.out), len(result)) + for i, v := range result { + assert.Equal(t, testCase.out[i], v) + } + }) + } +} diff --git a/utils/remove_from_slice.go b/utils/remove_from_slice.go new file mode 100644 index 0000000..5ac4dcb --- /dev/null +++ b/utils/remove_from_slice.go @@ -0,0 +1,9 @@ +package utils + +// RemoveFromSlice removes a value from slice at a given index +func RemoveFromSlice[T any](in []T, index int) []T { + if index >= len(in) { + return in + } + return append(in[:index], in[index+1:]...) +} diff --git a/utils/remove_from_slice_test.go b/utils/remove_from_slice_test.go new file mode 100644 index 0000000..f3b62e0 --- /dev/null +++ b/utils/remove_from_slice_test.go @@ -0,0 +1,44 @@ +package utils_test + +import ( + "testing" + + "github.com/aidenwallis/go-utils/internal/assert" + "github.com/aidenwallis/go-utils/utils" +) + +func TestRemoveFromSlice(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in []int + out []int + indexToRemove int + }{ + "normal operation": { + in: []int{0, 1, 2, 3}, + out: []int{0, 1, 3}, + indexToRemove: 2, + }, + + "does not panic when out of bounds": { + in: []int{0, 1, 2}, + out: []int{0, 1, 2}, + indexToRemove: 4, + }, + } + + for name, testCase := range testCases { + testCase := testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + result := utils.RemoveFromSlice(testCase.in, testCase.indexToRemove) + assert.Equal(t, len(testCase.out), len(result)) + for i, v := range result { + assert.Equal(t, testCase.out[i], v) + } + }) + } +}