Windows was dumb. Is now LF

This commit is contained in:
Samantha Becker 2023-08-18 17:13:53 +02:00
parent c50cf88dd0
commit 8b839f802b
6 changed files with 343 additions and 343 deletions

View file

@ -1,3 +1,3 @@
# GoUtils
A collection of useful functions and structs that I use for my projects.
# GoUtils
A collection of useful functions and structs that I use for my projects.

View file

@ -1,34 +1,34 @@
package containers
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,
}
}
package containers
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,
}
}

View file

@ -1,95 +1,95 @@
package containers
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,
}
}
package containers
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,
}
}

View file

@ -1,81 +1,81 @@
package containers
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,
}
}
package containers
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,
}
}

View file

@ -1,57 +1,57 @@
package maps
import "gitlab.com/beckersam/goutils/pkg/sliceutils"
// 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
}
package maps
import "gitlab.com/beckersam/goutils/pkg/sliceutils"
// 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
}

View file

@ -1,73 +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)
}
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)
}