Init go module and some files
Idk why maputils cant find sliceutils
This commit is contained in:
parent
ab331886fe
commit
e2e40cb56e
6 changed files with 341 additions and 0 deletions
3
go.mod
Normal file
3
go.mod
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module gitlab.com/beckersamuel9/utils
|
||||||
|
|
||||||
|
go 1.19
|
34
pkg/datastructs/generics.go
Normal file
34
pkg/datastructs/generics.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package datastructs
|
||||||
|
|
||||||
|
type ChainElem[T any] struct {
|
||||||
|
Elem *T
|
||||||
|
Next *ChainElem[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
// reachable checks if you can reach elem l when starting from elem f.
|
||||||
|
// It detects loops and returns false if it runs into one.
|
||||||
|
func reachable[T any](f, l *ChainElem[T]) bool {
|
||||||
|
// Map to keep track of nodes already visited
|
||||||
|
checks := make(map[*ChainElem[T]]bool)
|
||||||
|
for w := f; w != l; w = w.Next {
|
||||||
|
if w == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Shortcut: Maps where the value is bool have a default value of false
|
||||||
|
// If a key doesn't exist yet the result will thus be false
|
||||||
|
if checks[w] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Set the current elem to true to mark it as visited
|
||||||
|
checks[w] = true
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// emptyElem creates a new ChainElem[T] with empty values.
|
||||||
|
func emptyElem[T any]() *ChainElem[T] {
|
||||||
|
return &ChainElem[T]{
|
||||||
|
Elem: nil,
|
||||||
|
Next: nil,
|
||||||
|
}
|
||||||
|
}
|
95
pkg/datastructs/queues.go
Normal file
95
pkg/datastructs/queues.go
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
package datastructs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Queue[T any] struct {
|
||||||
|
head *ChainElem[T]
|
||||||
|
tail *ChainElem[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValid checks if the queue is still valid.
|
||||||
|
func (q *Queue[T]) isValid() bool {
|
||||||
|
return q.head != nil &&
|
||||||
|
q.tail != nil &&
|
||||||
|
reachable(q.head, q.tail)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty checks if the queue is empty.
|
||||||
|
func (q *Queue[T]) IsEmpty() bool {
|
||||||
|
return q.head == q.tail
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push adds a new element to the end of the queue.
|
||||||
|
func (q *Queue[T]) Push(elem *T) error {
|
||||||
|
if !q.isValid() {
|
||||||
|
return errors.New("invalid queue")
|
||||||
|
}
|
||||||
|
|
||||||
|
e := emptyElem[T]()
|
||||||
|
q.tail.Elem = elem
|
||||||
|
q.tail.Next = e
|
||||||
|
q.tail = e
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop removes the first element of the queue.
|
||||||
|
// It errors out if there is no element or the queue is invalid.
|
||||||
|
func (q *Queue[T]) Pop() (*T, error) {
|
||||||
|
if !q.isValid() {
|
||||||
|
return nil, errors.New("invalid queue")
|
||||||
|
}
|
||||||
|
if q.IsEmpty() {
|
||||||
|
return nil, errors.New("empty queue")
|
||||||
|
}
|
||||||
|
Elem := q.head.Elem
|
||||||
|
q.head = q.head.Next
|
||||||
|
return Elem, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top returns the first element of the queue without removing it.
|
||||||
|
// It errors out if there is no element or the queue is invalid.
|
||||||
|
func (q *Queue[T]) Top() (*T, error) {
|
||||||
|
if !q.isValid() {
|
||||||
|
return nil, errors.New("queue invalid")
|
||||||
|
}
|
||||||
|
if q.IsEmpty() {
|
||||||
|
return nil, errors.New("queue empty")
|
||||||
|
}
|
||||||
|
return q.head.Elem, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeadElem returns the first ChainElem of the queue without removing it.
|
||||||
|
// It errors out if there is no element or the queue is invalid.
|
||||||
|
func (q *Queue[T]) HeadElem() (*ChainElem[T], error) {
|
||||||
|
if !q.isValid() {
|
||||||
|
return nil, errors.New("queue invalid")
|
||||||
|
}
|
||||||
|
if q.IsEmpty() {
|
||||||
|
return nil, errors.New("queue empty")
|
||||||
|
}
|
||||||
|
return q.head, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON is used for generating json data when using json.Marshal.
|
||||||
|
func (q *Queue[T]) MarshalJSON() ([]byte, error) {
|
||||||
|
if !q.isValid() {
|
||||||
|
return nil, errors.New("queue invalid")
|
||||||
|
}
|
||||||
|
if q.IsEmpty() {
|
||||||
|
return nil, errors.New("queue empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(q.head)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildQueue[T any]() *Queue[T] {
|
||||||
|
empty := emptyElem[T]()
|
||||||
|
return &Queue[T]{
|
||||||
|
head: empty,
|
||||||
|
tail: empty,
|
||||||
|
}
|
||||||
|
}
|
81
pkg/datastructs/stacks.go
Normal file
81
pkg/datastructs/stacks.go
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
package DataStructs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Stack[T any] struct {
|
||||||
|
top *ChainElem[T]
|
||||||
|
bottom *ChainElem[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValid checks if the stack is valid.
|
||||||
|
func (s *Stack[T]) isValid() bool {
|
||||||
|
return s.top != nil &&
|
||||||
|
s.bottom != nil &&
|
||||||
|
reachable(s.top, s.bottom)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty checks if the stack is currently empty.
|
||||||
|
func (s *Stack[T]) IsEmpty() bool {
|
||||||
|
return s.top == s.bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push adds a new element to the top of the stack.
|
||||||
|
// It errors out if the stack is invalid.
|
||||||
|
func (s *Stack[T]) Push(e *T) error {
|
||||||
|
if !s.isValid() {
|
||||||
|
return errors.New("stack invalid")
|
||||||
|
}
|
||||||
|
n := emptyElem[T]()
|
||||||
|
n.Elem = e
|
||||||
|
n.Next = s.top
|
||||||
|
s.top = n
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop removes the first element at the top of the stack and returns it.
|
||||||
|
// It errors out if the stack is invalid or empty.
|
||||||
|
func (s *Stack[T]) Pop() (*T, error) {
|
||||||
|
if !s.isValid() {
|
||||||
|
return nil, errors.New("stack invalid")
|
||||||
|
}
|
||||||
|
if s.IsEmpty() {
|
||||||
|
return nil, errors.New("stack empty")
|
||||||
|
}
|
||||||
|
e := s.top.Elem
|
||||||
|
s.top = s.top.Next
|
||||||
|
return e, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top returns the first element at the top of the stack without removing it.
|
||||||
|
// It errors out if the stack is empty or invalid.
|
||||||
|
func (s *Stack[T]) Top() (*T, error) {
|
||||||
|
if !s.isValid() {
|
||||||
|
return nil, errors.New("stack invalid")
|
||||||
|
}
|
||||||
|
if s.IsEmpty() {
|
||||||
|
return nil, errors.New("stack empty")
|
||||||
|
}
|
||||||
|
return s.top.Elem, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON is used by json.Marshal to create a json representation.
|
||||||
|
func (s *Stack[T]) MarshalJSON() ([]byte, error) {
|
||||||
|
if !s.isValid() {
|
||||||
|
return nil, errors.New("queue invalid")
|
||||||
|
}
|
||||||
|
if s.IsEmpty() {
|
||||||
|
return nil, errors.New("queue empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(s.top)
|
||||||
|
}
|
||||||
|
func BuildStack[T any]() *Stack[T] {
|
||||||
|
empty := emptyElem[T]()
|
||||||
|
return &Stack[T]{
|
||||||
|
top: empty,
|
||||||
|
bottom: empty,
|
||||||
|
}
|
||||||
|
}
|
55
pkg/maputils/mapUtils.go
Normal file
55
pkg/maputils/mapUtils.go
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package maps
|
||||||
|
|
||||||
|
// MapMap applies a given function to every key-value pair of a map.
|
||||||
|
// The returned map's value type may be different from the type of the inital map's value.
|
||||||
|
func MapMap[K comparable, V any, R any](dic map[K]V, apply func(K, V) R) map[K]R {
|
||||||
|
n := make(map[K]R, len(dic))
|
||||||
|
for key, val := range dic {
|
||||||
|
n[key] = apply(key, val)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterMap filters a map using a given function.
|
||||||
|
// If the filter function returns true, the key-value pair stays, otherwise it gets removed.
|
||||||
|
func FilterMap[K comparable, V any](dic map[K]V, filter func(K, V) bool) map[K]V {
|
||||||
|
n := make(map[K]V, 0)
|
||||||
|
for key, val := range dic {
|
||||||
|
if filter(key, val) {
|
||||||
|
n[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeysFromMap creates a slice of keys that match the keys in a given map.
|
||||||
|
func KeysFromMap[K comparable, V any](m map[K]V) []K {
|
||||||
|
keys := make([]K, len(m))
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for k := range m {
|
||||||
|
keys[i] = k
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareMap compares two maps for key-val equality (If both maps have the same key-value pairs).
|
||||||
|
func CompareMap[K, V comparable](a, b map[K]V) bool {
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Check if both maps have the same keys
|
||||||
|
if !sliceutils.CompareUnorderedSlice(KeysFromMap(a), KeysFromMap(b)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then compare key-value pairs
|
||||||
|
for k, v := range a {
|
||||||
|
val, ok := b[k]
|
||||||
|
if !(ok && val == v) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
73
pkg/sliceutils/sliceUtils.go
Normal file
73
pkg/sliceutils/sliceUtils.go
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package sliceutils
|
||||||
|
|
||||||
|
// MapSlice applies a given function to every element of a slice.
|
||||||
|
// The return type may be different from the initial type of the slice.
|
||||||
|
func MapSlice[T any, M any](arr []T, apply func(T) M) []M {
|
||||||
|
n := make([]M, len(arr))
|
||||||
|
for i, e := range arr {
|
||||||
|
n[i] = apply(e)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterSlice filters a slice using a given function.
|
||||||
|
// If the filter function returns true, the element stays, otherwise it gets removed.
|
||||||
|
func FilterSlice[T any](arr []T, filter func(T) bool) []T {
|
||||||
|
n := make([]T, 0)
|
||||||
|
for _, e := range arr {
|
||||||
|
if filter(e) {
|
||||||
|
n = append(n, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveDuplicateSlice removes all duplicates inside a slice.
|
||||||
|
func RemoveDuplicateSlice[T comparable](sliceList []T) []T {
|
||||||
|
allKeys := make(map[T]bool)
|
||||||
|
list := []T{}
|
||||||
|
for _, item := range sliceList {
|
||||||
|
if _, value := allKeys[item]; !value {
|
||||||
|
allKeys[item] = true
|
||||||
|
list = append(list, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReverseSlice reverses a given slice.
|
||||||
|
func ReverseSlice[S ~[]E, E any](s S) {
|
||||||
|
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
s[i], s[j] = s[j], s[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareOrderedSlice compares two slices for both element equality and element order.
|
||||||
|
func CompareOrderedSlice[T comparable](a, b []T) bool {
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, v := range a {
|
||||||
|
if b[i] != v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareUnorderedSlice compares two slices for element equality.
|
||||||
|
// The order of those elements does not matter.
|
||||||
|
func CompareUnorderedSlice[T comparable](a, b []T) bool {
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
hits := 0
|
||||||
|
for _, v := range a {
|
||||||
|
for _, o := range b {
|
||||||
|
if o == v {
|
||||||
|
hits += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hits == len(a)
|
||||||
|
}
|
Loading…
Reference in a new issue