376 lines
12 KiB
Go
376 lines
12 KiB
Go
// 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 sliceutil
|
|
|
|
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
|
|
}
|