feat: dateutils, maputils, sliceutils.
This commit is contained in:
32
dateutils/dateutils.go
Normal file
32
dateutils/dateutils.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package dateutils
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Floor - takes a datetime and return a datetime from the same day at 00:00:00 (UTC).
|
||||||
|
func Floor(t time.Time) time.Time {
|
||||||
|
return t.UTC().Truncate(time.Hour * 24)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ceil - takes a datetime and return a datetime from the same day at 23:59:59 (UTC).
|
||||||
|
func Ceil(t time.Time) time.Time {
|
||||||
|
// add 24 hours so that we are dealing with tomorrow's datetime
|
||||||
|
// Floor
|
||||||
|
// Substract one second and we have today at 23:59:59
|
||||||
|
return Floor(t.Add(time.Hour * 24)).Add(time.Second * -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BeforeOrEqual(milestone time.Time, date time.Time) bool {
|
||||||
|
return date.UTC().Before(milestone) || date.UTC().Equal(milestone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AfterOrEqual(milestone time.Time, date time.Time) bool {
|
||||||
|
return date.UTC().After(milestone) || date.UTC().Equal(milestone)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overlap - returns true if two date intervals overlap.
|
||||||
|
func Overlap(start1 time.Time, end1 time.Time, start2 time.Time, end2 time.Time) bool {
|
||||||
|
return (AfterOrEqual(start2, start1) && BeforeOrEqual(end2, start1)) ||
|
||||||
|
(AfterOrEqual(start2, end1) && BeforeOrEqual(end2, end1)) ||
|
||||||
|
(AfterOrEqual(start1, start2) && BeforeOrEqual(end1, start2)) ||
|
||||||
|
(AfterOrEqual(start1, end2) && BeforeOrEqual(end1, end2))
|
||||||
|
}
|
||||||
66
dateutils/dateutils_test.go
Normal file
66
dateutils/dateutils_test.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package dateutils_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/tx7do/go-utils/dateutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFloor(t *testing.T) {
|
||||||
|
now := time.Now()
|
||||||
|
assert.Equal(t, "00:00:00", dateutils.Floor(now).Format("15:04:05"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCeil(t *testing.T) {
|
||||||
|
now := time.Now()
|
||||||
|
assert.Equal(t, "23:59:59", dateutils.Ceil(now).Format("15:04:05"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBeforeOrEqual(t *testing.T) {
|
||||||
|
milestone, _ := time.Parse("2006-01-02", "2023-01-01")
|
||||||
|
|
||||||
|
dBefore, _ := time.Parse("2006-01-02", "2022-12-31")
|
||||||
|
dEqual, _ := time.Parse("2006-01-02", "2023-01-01")
|
||||||
|
dAfter, _ := time.Parse("2006-01-02", "2023-01-31")
|
||||||
|
|
||||||
|
assert.Equal(t, true, dateutils.BeforeOrEqual(milestone, dBefore))
|
||||||
|
assert.Equal(t, true, dateutils.BeforeOrEqual(milestone, dEqual))
|
||||||
|
assert.Equal(t, false, dateutils.BeforeOrEqual(milestone, dAfter))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAfterOrEqual(t *testing.T) {
|
||||||
|
milestone, _ := time.Parse("2006-01-02", "2023-01-01")
|
||||||
|
|
||||||
|
dBefore, _ := time.Parse("2006-01-02", "2022-12-31")
|
||||||
|
dEqual, _ := time.Parse("2006-01-02", "2023-01-01")
|
||||||
|
dAfter, _ := time.Parse("2006-01-02", "2023-01-31")
|
||||||
|
|
||||||
|
assert.Equal(t, false, dateutils.AfterOrEqual(milestone, dBefore))
|
||||||
|
assert.Equal(t, true, dateutils.AfterOrEqual(milestone, dEqual))
|
||||||
|
assert.Equal(t, true, dateutils.AfterOrEqual(milestone, dAfter))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOverlap(t *testing.T) {
|
||||||
|
s1, _ := time.Parse("2006-01-02", "2022-12-28")
|
||||||
|
e1, _ := time.Parse("2006-01-02", "2022-12-31")
|
||||||
|
|
||||||
|
s2, _ := time.Parse("2006-01-02", "2022-12-30")
|
||||||
|
e2, _ := time.Parse("2006-01-02", "2023-01-01")
|
||||||
|
|
||||||
|
s3, _ := time.Parse("2006-01-02", "2023-01-02")
|
||||||
|
e3, _ := time.Parse("2006-01-02", "2023-01-04")
|
||||||
|
|
||||||
|
assert.Equal(t, true, dateutils.Overlap(s1, e1, s2, e2))
|
||||||
|
assert.Equal(t, false, dateutils.Overlap(s1, e1, s3, e3))
|
||||||
|
|
||||||
|
s4, _ := time.Parse("2006-01-02", "2023-07-13")
|
||||||
|
e4, _ := time.Parse("2006-01-02", "2023-07-14")
|
||||||
|
|
||||||
|
s5, _ := time.Parse("2006-01-02", "2023-07-10")
|
||||||
|
e5, _ := time.Parse("2006-01-02", "2023-07-17")
|
||||||
|
|
||||||
|
assert.Equal(t, true, dateutils.Overlap(s4, e4, s5, e5))
|
||||||
|
assert.Equal(t, true, dateutils.Overlap(s5, e5, s4, e4))
|
||||||
|
}
|
||||||
1
go.mod
1
go.mod
@@ -8,6 +8,7 @@ require (
|
|||||||
github.com/sony/sonyflake v1.2.0
|
github.com/sony/sonyflake v1.2.0
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
golang.org/x/crypto v0.14.0
|
golang.org/x/crypto v0.14.0
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -26,6 +26,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
|
|||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
|||||||
94
maputils/maputils.go
Normal file
94
maputils/maputils.go
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
// This package includes utility functions for handling and manipulating maputils.
|
||||||
|
// It draws inspiration from JavaScript and Python and uses Go generics as a basis.
|
||||||
|
|
||||||
|
package maputils
|
||||||
|
|
||||||
|
// Keys - takes a map with keys K and values V, returns a slice of type K of the map's keys.
|
||||||
|
// Note: Go maps do not preserve insertion order.
|
||||||
|
func Keys[K comparable, V any](mapInstance map[K]V) []K {
|
||||||
|
keys := make([]K, len(mapInstance))
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for k := range mapInstance {
|
||||||
|
keys[i] = k
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values - takes a map with keys K and values V, returns a slice of type V of the map's values.
|
||||||
|
// Note: Go maps do not preserve insertion order.
|
||||||
|
func Values[K comparable, V any](mapInstance map[K]V) []V {
|
||||||
|
values := make([]V, len(mapInstance))
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for _, v := range mapInstance {
|
||||||
|
values[i] = v
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge - takes an arbitrary number of map instances with keys K and values V and merges them into a single map.
|
||||||
|
// Note: merge works from left to right. If a key already exists in a previous map, its value is over-written.
|
||||||
|
func Merge[K comparable, V any](mapInstances ...map[K]V) map[K]V {
|
||||||
|
var mergedMapSize int
|
||||||
|
|
||||||
|
for _, mapInstance := range mapInstances {
|
||||||
|
mergedMapSize += len(mapInstance)
|
||||||
|
}
|
||||||
|
|
||||||
|
mergedMap := make(map[K]V, mergedMapSize)
|
||||||
|
|
||||||
|
for _, mapInstance := range mapInstances {
|
||||||
|
for k, v := range mapInstance {
|
||||||
|
mergedMap[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergedMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForEach - given a map with keys K and values V, executes the passed in function for each key-value pair.
|
||||||
|
func ForEach[K comparable, V any](mapInstance map[K]V, function func(key K, value V)) {
|
||||||
|
for key, value := range mapInstance {
|
||||||
|
function(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop - takes a map with keys K and values V, and a slice of keys K, dropping all the key-value pairs that match the keys in the slice.
|
||||||
|
// Note: this function will modify the passed in map. To get a different object, use the Copy function to pass a copy to this function.
|
||||||
|
func Drop[K comparable, V any](mapInstance map[K]V, keys []K) map[K]V {
|
||||||
|
for _, key := range keys {
|
||||||
|
delete(mapInstance, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy - takes a map with keys K and values V and returns a copy of the map.
|
||||||
|
func Copy[K comparable, V any](mapInstance map[K]V) map[K]V {
|
||||||
|
mapCopy := make(map[K]V, len(mapInstance))
|
||||||
|
|
||||||
|
for key, value := range mapInstance {
|
||||||
|
mapCopy[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapCopy
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter - takes a map with keys K and values V, and executes the passed in function for each key-value pair.
|
||||||
|
// If the filter function returns true, the key-value pair will be included in the output, otherwise it is filtered out.
|
||||||
|
func Filter[K comparable, V any](mapInstance map[K]V, function func(key K, value V) bool) map[K]V {
|
||||||
|
mapCopy := make(map[K]V, len(mapInstance))
|
||||||
|
|
||||||
|
for key, value := range mapInstance {
|
||||||
|
if function(key, value) {
|
||||||
|
mapCopy[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapCopy
|
||||||
|
}
|
||||||
139
maputils/maputils_test.go
Normal file
139
maputils/maputils_test.go
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
package maputils_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/tx7do/go-utils/maputils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestKeys(t *testing.T) {
|
||||||
|
var daysMap = map[string]int{
|
||||||
|
"Sunday": 1,
|
||||||
|
"Monday": 2,
|
||||||
|
"Tuesday": 3,
|
||||||
|
"Wednesday": 4,
|
||||||
|
"Thursday": 5,
|
||||||
|
"Friday": 6,
|
||||||
|
"Saturday": 7,
|
||||||
|
}
|
||||||
|
keys := maputils.Keys(daysMap)
|
||||||
|
days := []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}
|
||||||
|
sort.Strings(days)
|
||||||
|
sort.Strings(keys)
|
||||||
|
assert.Equal(t, days, keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValues(t *testing.T) {
|
||||||
|
var daysMap = map[string]int{
|
||||||
|
"Sunday": 1,
|
||||||
|
"Monday": 2,
|
||||||
|
"Tuesday": 3,
|
||||||
|
"Wednesday": 4,
|
||||||
|
"Thursday": 5,
|
||||||
|
"Friday": 6,
|
||||||
|
"Saturday": 7,
|
||||||
|
}
|
||||||
|
values := maputils.Values(daysMap)
|
||||||
|
sort.Ints(values)
|
||||||
|
assert.Equal(t, []int{1, 2, 3, 4, 5, 6, 7}, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMerge(t *testing.T) {
|
||||||
|
first := map[string]string{
|
||||||
|
"George": "Harrison",
|
||||||
|
"Betty": "Davis",
|
||||||
|
}
|
||||||
|
second := map[string]string{
|
||||||
|
"Ronald": "Reagen",
|
||||||
|
"Betty": "Stewart",
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t,
|
||||||
|
map[string]string{
|
||||||
|
"George": "Harrison",
|
||||||
|
"Betty": "Stewart",
|
||||||
|
"Ronald": "Reagen",
|
||||||
|
}, maputils.Merge(first, second))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestForEach(t *testing.T) {
|
||||||
|
var daysMap = map[string]int{
|
||||||
|
"Sunday": 1,
|
||||||
|
"Monday": 2,
|
||||||
|
"Tuesday": 3,
|
||||||
|
"Wednesday": 4,
|
||||||
|
"Thursday": 5,
|
||||||
|
"Friday": 6,
|
||||||
|
"Saturday": 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
sum := 0
|
||||||
|
|
||||||
|
maputils.ForEach(daysMap, func(_ string, day int) {
|
||||||
|
sum += day
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, 28, sum)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDrop(t *testing.T) {
|
||||||
|
var daysMap = map[string]int{
|
||||||
|
"Sunday": 1,
|
||||||
|
"Monday": 2,
|
||||||
|
"Tuesday": 3,
|
||||||
|
"Wednesday": 4,
|
||||||
|
"Thursday": 5,
|
||||||
|
"Friday": 6,
|
||||||
|
"Saturday": 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedResult := map[string]int{
|
||||||
|
"Sunday": 1,
|
||||||
|
"Friday": 6,
|
||||||
|
}
|
||||||
|
assert.Equal(t, expectedResult, maputils.Drop(daysMap, []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Saturday"}))
|
||||||
|
// ensure we do not modify the original value
|
||||||
|
assert.Equal(t, expectedResult, daysMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopy(t *testing.T) {
|
||||||
|
var daysMap = map[string]int{
|
||||||
|
"Sunday": 1,
|
||||||
|
"Monday": 2,
|
||||||
|
"Tuesday": 3,
|
||||||
|
"Wednesday": 4,
|
||||||
|
"Thursday": 5,
|
||||||
|
"Friday": 6,
|
||||||
|
"Saturday": 7,
|
||||||
|
}
|
||||||
|
daysCopy := maputils.Copy(daysMap)
|
||||||
|
assert.Equal(t, daysMap, daysCopy)
|
||||||
|
delete(daysCopy, "Sunday")
|
||||||
|
assert.NotEqual(t, daysMap, daysCopy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilter(t *testing.T) {
|
||||||
|
var daysMap = map[string]int{
|
||||||
|
"Sunday": 1,
|
||||||
|
"Monday": 2,
|
||||||
|
"Tuesday": 3,
|
||||||
|
"Wednesday": 4,
|
||||||
|
"Thursday": 5,
|
||||||
|
"Friday": 6,
|
||||||
|
"Saturday": 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedResult = map[string]int{
|
||||||
|
"Monday": 2,
|
||||||
|
"Wednesday": 4,
|
||||||
|
"Friday": 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredDays := maputils.Filter(daysMap, func(_ string, value int) bool {
|
||||||
|
return value%2 == 0
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, expectedResult, filteredDays)
|
||||||
|
}
|
||||||
375
sliceutils/sliceutils.go
Normal file
375
sliceutils/sliceutils.go
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
// This package includes utility functions for handling and manipulating a slice.
|
||||||
|
// It draws inspiration from JavaScript and Python and uses Go generics as a basis.
|
||||||
|
|
||||||
|
package sliceutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Filter - given a slice of type T, executes the given predicate function on each element in the slice.
|
||||||
|
// The predicate is passed the current element, the current index and the slice itself as function arguments.
|
||||||
|
// If the predicate returns true, the value is included in the result, otherwise it is filtered out.
|
||||||
|
func Filter[T any](slice []T, predicate func(value T, index int, slice []T) bool) (filtered []T) {
|
||||||
|
for i, el := range slice {
|
||||||
|
if ok := predicate(el, i, slice); ok {
|
||||||
|
filtered = append(filtered, el)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForEach - given a slice of type T, executes the passed in function for each element in the slice.
|
||||||
|
// The function is passed the current element, the current index and the slice itself as function arguments.
|
||||||
|
func ForEach[T any](slice []T, function func(value T, index int, slice []T)) {
|
||||||
|
for i, el := range slice {
|
||||||
|
function(el, i, slice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map - given a slice of type T, executes the passed in mapper function for each element in the slice, returning a slice of type R.
|
||||||
|
// The function is passed the current element, the current index and the slice itself as function arguments.
|
||||||
|
func Map[T any, R any](slice []T, mapper func(value T, index int, slice []T) R) (mapped []R) {
|
||||||
|
if len(slice) > 0 {
|
||||||
|
mapped = make([]R, len(slice))
|
||||||
|
for i, el := range slice {
|
||||||
|
mapped[i] = mapper(el, i, slice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mapped
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reduce - given a slice of type T, executes the passed in reducer function for each element in the slice, returning a result of type R.
|
||||||
|
// The function is passed the accumulator, current element, current index and the slice itself as function arguments.
|
||||||
|
// The third argument to reduce is the initial value of type R to use.
|
||||||
|
func Reduce[T any, R any](slice []T, reducer func(acc R, value T, index int, slice []T) R, initial R) R {
|
||||||
|
acc := initial
|
||||||
|
for i, el := range slice {
|
||||||
|
acc = reducer(acc, el, i, slice)
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find - given a slice of type T, executes the passed in predicate function for each element in the slice.
|
||||||
|
// If the predicate returns true - a pointer to the element is returned. If no element is found, nil is returned.
|
||||||
|
// The function is passed the current element, the current index and the slice itself as function arguments.
|
||||||
|
func Find[T any](slice []T, predicate func(value T, index int, slice []T) bool) *T {
|
||||||
|
for i, el := range slice {
|
||||||
|
if ok := predicate(el, i, slice); ok {
|
||||||
|
return &el
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindIndex - given a slice of type T, executes the passed in predicate function for each element in the slice.
|
||||||
|
// If the predicate returns true - the index of the element is returned. If no element is found, -1 is returned.
|
||||||
|
// The function is passed the current element, the current index and the slice itself as function arguments.
|
||||||
|
func FindIndex[T any](slice []T, predicate func(value T, index int, slice []T) bool) int {
|
||||||
|
for i, el := range slice {
|
||||||
|
if ok := predicate(el, i, slice); ok {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindIndexOf - given a slice of type T and a value of type T, return ths first index of an element equal to value.
|
||||||
|
// If no element is found, -1 is returned.
|
||||||
|
func FindIndexOf[T comparable](slice []T, value T) int {
|
||||||
|
for i, el := range slice {
|
||||||
|
if el == value {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastIndex - given a slice of type T, executes the passed in predicate function for each element in the slice starting from its end.
|
||||||
|
// If no element is found, -1 is returned. The function is passed the current element, the current index and the slice itself as function arguments.
|
||||||
|
func FindLastIndex[T any](slice []T, predicate func(value T, index int, slice []T) bool) int {
|
||||||
|
for i := len(slice) - 1; i > 0; i-- {
|
||||||
|
el := slice[i]
|
||||||
|
if ok := predicate(el, i, slice); ok {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastIndexOf - given a slice of type T and a value of type T, returning the last index matching the given value.
|
||||||
|
// If no element is found, -1 is returned.
|
||||||
|
func FindLastIndexOf[T comparable](slice []T, value T) int {
|
||||||
|
for i := len(slice) - 1; i > 0; i-- {
|
||||||
|
el := slice[i]
|
||||||
|
if el == value {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindIndexes - given a slice of type T, executes the passed in predicate function for each element in the slice.
|
||||||
|
// Returns a slice containing all indexes of elements for which the predicate returned true. If no element are found, returns a nil int slice.
|
||||||
|
// The function is passed the current element, the current index and the slice itself as function arguments.
|
||||||
|
func FindIndexes[T any](slice []T, predicate func(value T, index int, slice []T) bool) []int {
|
||||||
|
var indexes []int
|
||||||
|
for i, el := range slice {
|
||||||
|
if ok := predicate(el, i, slice); ok {
|
||||||
|
indexes = append(indexes, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return indexes
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindIndexesOf - given a slice of type T and a value of type T, returns a slice containing all indexes match the given value.
|
||||||
|
// If no element are found, returns a nil int slice.
|
||||||
|
func FindIndexesOf[T comparable](slice []T, value T) []int {
|
||||||
|
var indexes []int
|
||||||
|
for i, el := range slice {
|
||||||
|
if el == value {
|
||||||
|
indexes = append(indexes, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return indexes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Includes - given a slice of type T and a value of type T, determines whether the value is contained by the slice.
|
||||||
|
// Note: T is constrained to comparable types only and comparison is determined using the equality operator.
|
||||||
|
func Includes[T comparable](slice []T, value T) bool {
|
||||||
|
for _, el := range slice {
|
||||||
|
if el == value {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some - given a slice of type T, executes the given predicate for each element of the slice.
|
||||||
|
// If the predicate returns true for any element, it returns true, otherwise it returns false.
|
||||||
|
// The function is passed the current element, the current index and the slice itself as function arguments.
|
||||||
|
func Some[T any](slice []T, predicate func(value T, index int, slice []T) bool) bool {
|
||||||
|
for i, el := range slice {
|
||||||
|
if ok := predicate(el, i, slice); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Every - given a slice of type T, executes the given predicate for each element of the slice.
|
||||||
|
// If the predicate returns true for all elements, it returns true, otherwise it returns false.
|
||||||
|
// The function is passed the current element, the current index and the slice itself as function arguments.
|
||||||
|
func Every[T any](slice []T, predicate func(value T, index int, slice []T) bool) bool {
|
||||||
|
for i, el := range slice {
|
||||||
|
if ok := predicate(el, i, slice); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge - receives slices of type T and merges them into a single slice of type T.
|
||||||
|
// Note: The elements are merged in their order in a slice,
|
||||||
|
// i.e. first the elements of the first slice, then that of the second and so forth.
|
||||||
|
func Merge[T any](slices ...[]T) (mergedSlice []T) {
|
||||||
|
if len(slices) > 0 {
|
||||||
|
mergedSliceCap := 0
|
||||||
|
|
||||||
|
for _, slice := range slices {
|
||||||
|
mergedSliceCap += len(slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
if mergedSliceCap > 0 {
|
||||||
|
mergedSlice = make([]T, 0, mergedSliceCap)
|
||||||
|
|
||||||
|
for _, slice := range slices {
|
||||||
|
mergedSlice = append(mergedSlice, slice...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mergedSlice
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sum - receives a slice of type T and returns a value T that is the sum of the numbers.
|
||||||
|
// Note: T is constrained to be a number type.
|
||||||
|
func Sum[T constraints.Complex | constraints.Integer | constraints.Float](slice []T) (result T) {
|
||||||
|
for _, el := range slice {
|
||||||
|
result += el
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove - receives a slice of type T and an index, removing the element at the given index.
|
||||||
|
// Note: this function does not modify the input slice.
|
||||||
|
func Remove[T any](slice []T, i int) []T {
|
||||||
|
if len(slice) == 0 || i > len(slice)-1 {
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
copied := Copy(slice)
|
||||||
|
if i == 0 {
|
||||||
|
return copied[1:]
|
||||||
|
}
|
||||||
|
if i != len(copied)-1 {
|
||||||
|
return append(copied[:i], copied[i+1:]...)
|
||||||
|
}
|
||||||
|
return copied[:i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert - receives a slice of type T, an index and a value.
|
||||||
|
// The value is inserted at the given index. If there is an existing value at this index, it is shifted to the next index.
|
||||||
|
// Note: this function does not modify the input slice.
|
||||||
|
func Insert[T any](slice []T, i int, value T) []T {
|
||||||
|
if len(slice) == i {
|
||||||
|
return append(slice, value)
|
||||||
|
}
|
||||||
|
slice = append(slice[:i+1], slice[i:]...)
|
||||||
|
slice[i] = value
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy - receives a slice of type T and copies it.
|
||||||
|
func Copy[T any](slice []T) []T {
|
||||||
|
duplicate := make([]T, len(slice), cap(slice))
|
||||||
|
copy(duplicate, slice)
|
||||||
|
return duplicate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intersection - takes a variadic number of slices of type T and returns a slice of type T containing any values that exist in all the slices.
|
||||||
|
// For example, given []int{1, 2, 3}, []int{1, 7, 3}, the intersection would be []int{1, 3}.
|
||||||
|
func Intersection[T comparable](slices ...[]T) []T {
|
||||||
|
possibleIntersections := map[T]int{}
|
||||||
|
for i, slice := range slices {
|
||||||
|
for _, el := range slice {
|
||||||
|
if i == 0 {
|
||||||
|
possibleIntersections[el] = 0
|
||||||
|
} else if _, elementExists := possibleIntersections[el]; elementExists {
|
||||||
|
possibleIntersections[el] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
intersected := make([]T, 0)
|
||||||
|
for _, el := range slices[0] {
|
||||||
|
if lastVisitorIndex, exists := possibleIntersections[el]; exists && lastVisitorIndex == len(slices)-1 {
|
||||||
|
intersected = append(intersected, el)
|
||||||
|
delete(possibleIntersections, el)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return intersected
|
||||||
|
}
|
||||||
|
|
||||||
|
// Difference - takes a variadic number of slices of type T and returns a slice of type T containing the elements that are different between the slices.
|
||||||
|
// For example, given []int{1, 2, 3}, []int{2, 3, 4}, []{3, 4, 5}, the difference would be []int{1, 5}.
|
||||||
|
func Difference[T comparable](slices ...[]T) []T {
|
||||||
|
possibleDifferences := map[T]int{}
|
||||||
|
nonDifferentElements := map[T]int{}
|
||||||
|
|
||||||
|
for i, slice := range slices {
|
||||||
|
for _, el := range slice {
|
||||||
|
if lastVisitorIndex, elementExists := possibleDifferences[el]; elementExists && lastVisitorIndex != i {
|
||||||
|
nonDifferentElements[el] = i
|
||||||
|
} else if !elementExists {
|
||||||
|
possibleDifferences[el] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
differentElements := make([]T, 0)
|
||||||
|
|
||||||
|
for _, slice := range slices {
|
||||||
|
for _, el := range slice {
|
||||||
|
if _, exists := nonDifferentElements[el]; !exists {
|
||||||
|
differentElements = append(differentElements, el)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return differentElements
|
||||||
|
}
|
||||||
|
|
||||||
|
// Union - takes a variadic number of slices of type T and returns a slice of type T containing the unique elements in the different slices
|
||||||
|
// For example, given []int{1, 2, 3}, []int{2, 3, 4}, []int{3, 4, 5}, the union would be []int{1, 2, 3, 4, 5}.
|
||||||
|
func Union[T comparable](slices ...[]T) []T {
|
||||||
|
return Unique(Merge(slices...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse - takes a slice of type T and returns a slice of type T with a reverse order of elements.
|
||||||
|
func Reverse[T any](slice []T) []T {
|
||||||
|
result := make([]T, len(slice))
|
||||||
|
|
||||||
|
itemCount := len(slice)
|
||||||
|
middle := itemCount / 2
|
||||||
|
result[middle] = slice[middle]
|
||||||
|
|
||||||
|
for i := 0; i < middle; i++ {
|
||||||
|
mirrorIdx := itemCount - i - 1
|
||||||
|
result[i], result[mirrorIdx] = slice[mirrorIdx], slice[i]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unique - receives a slice of type T and returns a slice of type T containing all unique elements.
|
||||||
|
func Unique[T comparable](slice []T) []T {
|
||||||
|
unique := make([]T, 0)
|
||||||
|
visited := map[T]bool{}
|
||||||
|
|
||||||
|
for _, value := range slice {
|
||||||
|
if exists := visited[value]; !exists {
|
||||||
|
unique = append(unique, value)
|
||||||
|
visited[value] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unique
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chunk - receives a slice of type T and size N and returns a slice of slices T of size N.
|
||||||
|
func Chunk[T any](input []T, size int) [][]T {
|
||||||
|
var chunks [][]T
|
||||||
|
|
||||||
|
for i := 0; i < len(input); i += size {
|
||||||
|
end := i + size
|
||||||
|
if end > len(input) {
|
||||||
|
end = len(input)
|
||||||
|
}
|
||||||
|
chunks = append(chunks, input[i:end])
|
||||||
|
}
|
||||||
|
return chunks
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pluck - receives a slice of type I and a getter func to a field
|
||||||
|
// and returns a slice containing the requested field's value from each item in the slice.
|
||||||
|
func Pluck[I any, O any](input []I, getter func(I) *O) []O {
|
||||||
|
var output []O
|
||||||
|
|
||||||
|
for _, item := range input {
|
||||||
|
field := getter(item)
|
||||||
|
|
||||||
|
if field != nil {
|
||||||
|
output = append(output, *field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flatten - receives a slice of slices of type I and flattens it to a slice of type I.
|
||||||
|
func Flatten[I any](input [][]I) (output []I) {
|
||||||
|
if len(input) > 0 {
|
||||||
|
var outputSize int
|
||||||
|
|
||||||
|
for _, item := range input {
|
||||||
|
outputSize += len(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if outputSize > 0 {
|
||||||
|
output = make([]I, 0, outputSize)
|
||||||
|
|
||||||
|
for _, item := range input {
|
||||||
|
output = append(output, item...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
301
sliceutils/sliceutils_test.go
Normal file
301
sliceutils/sliceutils_test.go
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
package sliceutils_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/tx7do/go-utils/sliceutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyInt int
|
||||||
|
|
||||||
|
type Pluckable struct {
|
||||||
|
Code string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
var numerals = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||||
|
var numeralsWithUserDefinedType = []MyInt{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||||
|
var days = []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}
|
||||||
|
var lastNames = []string{"Jacobs", "Vin", "Jacobs", "Smith"}
|
||||||
|
|
||||||
|
func TestFilter(t *testing.T) {
|
||||||
|
expectedResult := []int{0, 2, 4, 6, 8}
|
||||||
|
actualResult := sliceutils.Filter(numerals, func(value int, _ int, _ []int) bool {
|
||||||
|
return value%2 == 0
|
||||||
|
})
|
||||||
|
assert.Equal(t, expectedResult, actualResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestForEach(t *testing.T) {
|
||||||
|
result := 0
|
||||||
|
sliceutils.ForEach(numerals, func(value int, _ int, _ []int) {
|
||||||
|
result += value
|
||||||
|
})
|
||||||
|
assert.Equal(t, 45, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMap(t *testing.T) {
|
||||||
|
expectedResult := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||||
|
actualResult := sliceutils.Map(numerals, func(value int, _ int, _ []int) string {
|
||||||
|
return strconv.Itoa(value)
|
||||||
|
})
|
||||||
|
assert.Equal(t, expectedResult, actualResult)
|
||||||
|
|
||||||
|
assert.Nil(t, sliceutils.Map([]int{}, func(_ int, _ int, _ []int) string {
|
||||||
|
return ""
|
||||||
|
}))
|
||||||
|
|
||||||
|
assert.Nil(t, sliceutils.Map([]int(nil), func(_ int, _ int, _ []int) string {
|
||||||
|
return ""
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReduce(t *testing.T) {
|
||||||
|
expectedResult := map[string]string{"result": "0123456789"}
|
||||||
|
actualResult := sliceutils.Reduce(
|
||||||
|
numerals,
|
||||||
|
func(acc map[string]string, cur int, _ int, _ []int) map[string]string {
|
||||||
|
acc["result"] += strconv.Itoa(cur)
|
||||||
|
return acc
|
||||||
|
},
|
||||||
|
map[string]string{"result": ""},
|
||||||
|
)
|
||||||
|
assert.Equal(t, expectedResult, actualResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFind(t *testing.T) {
|
||||||
|
expectedResult := "Wednesday"
|
||||||
|
actualResult := sliceutils.Find(days, func(value string, index int, slice []string) bool {
|
||||||
|
return strings.Contains(value, "Wed")
|
||||||
|
})
|
||||||
|
assert.Equal(t, expectedResult, *actualResult)
|
||||||
|
assert.Nil(t, sliceutils.Find(days, func(value string, index int, slice []string) bool {
|
||||||
|
return strings.Contains(value, "Rishon")
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindIndex(t *testing.T) {
|
||||||
|
expectedResult := 3
|
||||||
|
actualResult := sliceutils.FindIndex(days, func(value string, index int, slice []string) bool {
|
||||||
|
return strings.Contains(value, "Wed")
|
||||||
|
})
|
||||||
|
assert.Equal(t, expectedResult, actualResult)
|
||||||
|
assert.Equal(t, -1, sliceutils.FindIndex(days, func(value string, index int, slice []string) bool {
|
||||||
|
return strings.Contains(value, "Rishon")
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindIndexOf(t *testing.T) {
|
||||||
|
expectedResult := 3
|
||||||
|
actualResult := sliceutils.FindIndexOf(days, "Wednesday")
|
||||||
|
assert.Equal(t, expectedResult, actualResult)
|
||||||
|
assert.Equal(t, -1, sliceutils.FindIndexOf(days, "Rishon"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindLastIndex(t *testing.T) {
|
||||||
|
expectedResult := 2
|
||||||
|
actualResult := sliceutils.FindLastIndex(lastNames, func(value string, index int, slice []string) bool {
|
||||||
|
return value == "Jacobs"
|
||||||
|
})
|
||||||
|
assert.Equal(t, expectedResult, actualResult)
|
||||||
|
assert.Equal(t, -1, sliceutils.FindLastIndex(lastNames, func(value string, index int, slice []string) bool {
|
||||||
|
return value == "Hamudi"
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindLastIndexOf(t *testing.T) {
|
||||||
|
expectedResult := 2
|
||||||
|
actualResult := sliceutils.FindLastIndexOf(lastNames, "Jacobs")
|
||||||
|
assert.Equal(t, expectedResult, actualResult)
|
||||||
|
assert.Equal(t, -1, sliceutils.FindLastIndexOf(lastNames, "Hamudi"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindIndexes(t *testing.T) {
|
||||||
|
expectedResult := []int{0, 2}
|
||||||
|
actualResult := sliceutils.FindIndexes(lastNames, func(value string, index int, slice []string) bool {
|
||||||
|
return value == "Jacobs"
|
||||||
|
})
|
||||||
|
assert.Equal(t, expectedResult, actualResult)
|
||||||
|
assert.Nil(t, sliceutils.FindIndexes(lastNames, func(value string, index int, slice []string) bool {
|
||||||
|
return value == "Hamudi"
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindIndexesOf(t *testing.T) {
|
||||||
|
expectedResult := []int{0, 2}
|
||||||
|
actualResult := sliceutils.FindIndexesOf(lastNames, "Jacobs")
|
||||||
|
assert.Equal(t, expectedResult, actualResult)
|
||||||
|
assert.Nil(t, sliceutils.FindIndexesOf(lastNames, "Hamudi"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIncludes(t *testing.T) {
|
||||||
|
assert.True(t, sliceutils.Includes(numerals, 1))
|
||||||
|
assert.False(t, sliceutils.Includes(numerals, 11))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAny(t *testing.T) {
|
||||||
|
assert.True(t, sliceutils.Some(numerals, func(value int, _ int, _ []int) bool {
|
||||||
|
return value%5 == 0
|
||||||
|
}))
|
||||||
|
assert.False(t, sliceutils.Some(numerals, func(value int, _ int, _ []int) bool {
|
||||||
|
return value == 11
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAll(t *testing.T) {
|
||||||
|
assert.True(t, sliceutils.Every([]int{1, 1, 1}, func(value int, _ int, _ []int) bool {
|
||||||
|
return value == 1
|
||||||
|
}))
|
||||||
|
assert.False(t, sliceutils.Every([]int{1, 1, 1, 2}, func(value int, _ int, _ []int) bool {
|
||||||
|
return value == 1
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMerge(t *testing.T) {
|
||||||
|
result := sliceutils.Merge(numerals[:5], numerals[5:])
|
||||||
|
assert.Equal(t, numerals, result)
|
||||||
|
|
||||||
|
assert.Nil(t, sliceutils.Merge([]int(nil), []int(nil)))
|
||||||
|
assert.Nil(t, sliceutils.Merge([]int{}, []int{}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSum(t *testing.T) {
|
||||||
|
result := sliceutils.Sum(numerals)
|
||||||
|
assert.Equal(t, 45, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSum2(t *testing.T) {
|
||||||
|
result := sliceutils.Sum(numeralsWithUserDefinedType)
|
||||||
|
assert.Equal(t, MyInt(45), result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemove(t *testing.T) {
|
||||||
|
testSlice := []int{1, 2, 3}
|
||||||
|
result := sliceutils.Remove(testSlice, 1)
|
||||||
|
assert.Equal(t, []int{1, 3}, result)
|
||||||
|
assert.Equal(t, []int{1, 2, 3}, testSlice)
|
||||||
|
result = sliceutils.Remove(result, 1)
|
||||||
|
assert.Equal(t, []int{1}, result)
|
||||||
|
result = sliceutils.Remove(result, 3)
|
||||||
|
assert.Equal(t, []int{1}, result)
|
||||||
|
result = sliceutils.Remove(result, 0)
|
||||||
|
assert.Equal(t, []int{}, result)
|
||||||
|
result = sliceutils.Remove(result, 1)
|
||||||
|
assert.Equal(t, []int{}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopy(t *testing.T) {
|
||||||
|
testSlice := []int{1, 2, 3}
|
||||||
|
copiedSlice := sliceutils.Copy(testSlice)
|
||||||
|
copiedSlice[0] = 2
|
||||||
|
assert.NotEqual(t, testSlice, copiedSlice)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsert(t *testing.T) {
|
||||||
|
testSlice := []int{1, 2}
|
||||||
|
result := sliceutils.Insert(testSlice, 0, 3)
|
||||||
|
assert.Equal(t, []int{3, 1, 2}, result)
|
||||||
|
assert.NotEqual(t, testSlice, result)
|
||||||
|
assert.Equal(t, []int{1, 3, 2}, sliceutils.Insert(testSlice, 1, 3))
|
||||||
|
assert.Equal(t, []int{1, 2, 3}, sliceutils.Insert(testSlice, 2, 3))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntersection(t *testing.T) {
|
||||||
|
expectedResult := []int{3, 4, 5}
|
||||||
|
|
||||||
|
first := []int{1, 2, 3, 4, 5}
|
||||||
|
second := []int{2, 3, 4, 5, 6}
|
||||||
|
third := []int{3, 4, 5, 6, 7}
|
||||||
|
|
||||||
|
assert.Equal(t, expectedResult, sliceutils.Intersection(first, second, third))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDifference(t *testing.T) {
|
||||||
|
expectedResult := []int{1, 7}
|
||||||
|
|
||||||
|
first := []int{1, 2, 3, 4, 5}
|
||||||
|
second := []int{2, 3, 4, 5, 6}
|
||||||
|
third := []int{3, 4, 5, 6, 7}
|
||||||
|
|
||||||
|
assert.Equal(t, expectedResult, sliceutils.Difference(first, second, third))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnion(t *testing.T) {
|
||||||
|
expectedResult := []int{1, 2, 3, 4, 5, 6, 7}
|
||||||
|
|
||||||
|
first := []int{1, 2, 3, 4, 5}
|
||||||
|
second := []int{2, 3, 4, 5, 6}
|
||||||
|
third := []int{3, 4, 5, 6, 7}
|
||||||
|
|
||||||
|
assert.Equal(t, expectedResult, sliceutils.Union(first, second, third))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReverse(t *testing.T) {
|
||||||
|
expectedResult := []int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
|
||||||
|
assert.Equal(t, expectedResult, sliceutils.Reverse(numerals))
|
||||||
|
// ensure does not modify the original
|
||||||
|
assert.Equal(t, expectedResult, sliceutils.Reverse(numerals))
|
||||||
|
|
||||||
|
// test basic odd length case
|
||||||
|
expectedResult = []int{9, 8, 7, 6, 5, 4, 3, 2, 1}
|
||||||
|
assert.Equal(t, expectedResult, sliceutils.Reverse(numerals[1:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnique(t *testing.T) {
|
||||||
|
duplicates := []int{6, 6, 6, 9, 0, 0, 0}
|
||||||
|
expectedResult := []int{6, 9, 0}
|
||||||
|
assert.Equal(t, expectedResult, sliceutils.Unique(duplicates))
|
||||||
|
// Ensure original is unaltered
|
||||||
|
assert.NotEqual(t, expectedResult, duplicates)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChunk(t *testing.T) {
|
||||||
|
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
|
||||||
|
assert.Equal(t, [][]int{{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}}, sliceutils.Chunk(numbers, 2))
|
||||||
|
assert.Equal(t, [][]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10}}, sliceutils.Chunk(numbers, 3))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPluck(t *testing.T) {
|
||||||
|
items := []Pluckable{
|
||||||
|
{
|
||||||
|
Code: "azer",
|
||||||
|
Value: "Azer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Code: "tyuio",
|
||||||
|
Value: "Tyuio",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, []string{"azer", "tyuio"}, sliceutils.Pluck(items, func(item Pluckable) *string {
|
||||||
|
return &item.Code
|
||||||
|
}))
|
||||||
|
assert.Equal(t, []string{"Azer", "Tyuio"}, sliceutils.Pluck(items, func(item Pluckable) *string {
|
||||||
|
return &item.Value
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlatten(t *testing.T) {
|
||||||
|
items := [][]int{
|
||||||
|
{1, 2, 3, 4},
|
||||||
|
{5, 6},
|
||||||
|
{7, 8},
|
||||||
|
{9, 10, 11},
|
||||||
|
}
|
||||||
|
|
||||||
|
flattened := sliceutils.Flatten(items)
|
||||||
|
|
||||||
|
assert.Equal(t, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, flattened)
|
||||||
|
|
||||||
|
assert.Nil(t, sliceutils.Flatten([][]int{}))
|
||||||
|
assert.Nil(t, sliceutils.Flatten([][]int(nil)))
|
||||||
|
|
||||||
|
assert.Nil(t, sliceutils.Flatten([][]int{{}, {}}))
|
||||||
|
assert.Nil(t, sliceutils.Flatten([][]int{nil, nil}))
|
||||||
|
|
||||||
|
assert.Nil(t, sliceutils.Flatten([][]int{{}, nil}))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user