~loges/aoc-go-utils

746c819f6e229a9f98d24b7703b12815321f3154 — Logan Connolly 7 months ago f6183e9 v0.1.0
feat: port over utils used in aoc 2022
6 files changed, 190 insertions(+), 0 deletions(-)

A cast.go
A io.go
A math.go
A ops.go
A ops_test.go
A sets.go
A cast.go => cast.go +34 -0
@@ 0,0 1,34 @@
package aoc_go_utils

import (
	"log"
	"strconv"
)

// ToInt is a helper function for casting string values
// to base 10 integers.
func ToInt(s string) int {
	number, err := strconv.Atoi(s)
	if err != nil {
		log.Fatal(err)
	}
	return number
}

// ToSet takes a slice as input and outputs a set.
func ToSet[T comparable](s []T) map[T]bool {
	setMap := make(map[T]bool)
	for _, item := range s {
		setMap[item] = true
	}
	return setMap
}

// KeysToSlice converts map keys into a slice.
func KeysToSlice[K comparable, V any](m map[K]V) []K {
	keys := make([]K, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}
	return keys
}

A io.go => io.go +24 -0
@@ 0,0 1,24 @@
package aoc_go_utils

import (
	"log"
	"os"
	"strings"
)

// ReadInputAsString is a helper function for reading in the
// problem input and converting it to string.
func ReadInputAsString() string {
	content, err := os.ReadFile("./input.txt")
	if err != nil {
		log.Fatal(err)
	}
	return strings.TrimRight(string(content[:]), "\n")
}

// ReadInputAsStringLines is a helper function for reading in the
// problem input and converting it to a slice of strings.
func ReadInputAsStringLines(delim string) []string {
	s := ReadInputAsString()
	return strings.Split(s, delim)
}

A math.go => math.go +11 -0
@@ 0,0 1,11 @@
package aoc_go_utils

// SumInts sums a slice of integers.
func SumInts(ns []int) int {
	var total int
	for _, n := range ns {
		total += n
	}

	return total
}

A ops.go => ops.go +46 -0
@@ 0,0 1,46 @@
package aoc_go_utils

// Split takes a slice and splits it into two. The caller
// can specify a `ratio` parameter to change the lengths
// of each split
func Split[T any](s []T, ratio float64) ([]T, []T) {
	sliceLength := float64(len(s))
	midpoint := int(sliceLength * ratio)
	return s[:midpoint], s[midpoint:]
}

// Contains takes a slice and a value and returns true
// if it exists, else false.
func Contains[T comparable](s []T, value T) bool {
	for _, item := range s {
		if item == value {
			return true
		}
	}
	return false
}

// FindMatches iterates through the items in the shorter
// slice and returns all the matches that it finds.
func FindMatches[T comparable](s1, s2 []T) []T {
	if len(s2) < len(s1) {
		s1, s2 = s2, s1
	}

	matchSet := make(map[T]bool)
	for _, item := range s1 {
		_, exists := matchSet[item]
		if !exists && Contains(s2, item) {
			matchSet[item] = true
		}
	}

	return KeysToSlice(matchSet)
}

// Reverse flip the order of items in a slice.
func Reverse[T comparable](input []T) {
	for i, j := 0, len(input)-1; i < j; i, j = i+1, j-1 {
		input[i], input[j] = input[j], input[i]
	}
}

A ops_test.go => ops_test.go +45 -0
@@ 0,0 1,45 @@
package aoc_go_utils_test

import (
	"reflect"
	"testing"

	utils "git.sr.ht/~loges/aoc-go-utils"
)

func Test_Split(t *testing.T) {
	tests := []struct {
		input     []string
		ratio     float64
		wantLeft  []string
		wantRight []string
	}{
		{
			input:     []string{"a", "b"},
			ratio:     0.5,
			wantLeft:  []string{"a"},
			wantRight: []string{"b"},
		},
		{
			input:     []string{"a", "b", "c", "d"},
			ratio:     0.25,
			wantLeft:  []string{"a"},
			wantRight: []string{"b", "c", "d"},
		},
		{
			input:     []string{"a", "b", "c", "d"},
			ratio:     0.75,
			wantLeft:  []string{"a", "b", "c"},
			wantRight: []string{"d"},
		},
	}
	for _, tt := range tests {
		left, right := utils.Split(tt.input, tt.ratio)
		if !reflect.DeepEqual(tt.wantLeft, left) {
			t.Fatalf("expected: %v, got: %v on the left side", tt.wantLeft, left)
		}
		if !reflect.DeepEqual(tt.wantRight, right) {
			t.Fatalf("expected: %v, got: %v on the right side", tt.wantRight, right)
		}
	}
}

A sets.go => sets.go +30 -0
@@ 0,0 1,30 @@
package aoc_go_utils

// Intersect takes two sets and returns a set with the
// shared items.
func Intersect[T comparable](s1, s2 map[T]bool) map[T]bool {
	if len(s2) < len(s1) {
		s1, s2 = s2, s1
	}
	setMap := make(map[T]bool)
	for key := range s1 {
		_, ok := s2[key]
		if ok {
			setMap[key] = true
		}
	}
	return setMap
}

// Intersect takes two sets and returns a set with the
// shared items.
func Union[T comparable](s1, s2 map[T]bool) map[T]bool {
	setMap := make(map[T]bool)
	for key := range s1 {
		setMap[key] = true
	}
	for key := range s2 {
		setMap[key] = true
	}
	return setMap
}