initial commit
This commit is contained in:
26
pkg/array/aggregate.go
Normal file
26
pkg/array/aggregate.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package array
|
||||
|
||||
func Chunk[T interface{}](arr []T, chunkSize int) [][]T {
|
||||
var chunkedArray [][]T
|
||||
|
||||
for i := 0; i < len(arr); i += chunkSize {
|
||||
end := i + chunkSize
|
||||
|
||||
if end > len(arr) {
|
||||
end = len(arr)
|
||||
}
|
||||
|
||||
chunkedArray = append(chunkedArray, arr[i:end])
|
||||
}
|
||||
|
||||
return chunkedArray
|
||||
}
|
||||
|
||||
func Sum[T any, N Numbers](arr []T, selector func(val T) N) N {
|
||||
var summed N
|
||||
for i := 0; i < len(arr); i++ {
|
||||
r := selector(arr[i])
|
||||
summed += r
|
||||
}
|
||||
return summed
|
||||
}
|
||||
30
pkg/array/aggregate_test.go
Normal file
30
pkg/array/aggregate_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSum_WithNumberArray_ShouldBeAsExpected(t *testing.T) {
|
||||
// Arrange
|
||||
arr := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||
// Act
|
||||
r := Sum(arr, func(val int) int {
|
||||
return val
|
||||
})
|
||||
// Assert
|
||||
const expected = 55
|
||||
assert.True(t, r == expected)
|
||||
}
|
||||
|
||||
func TestSum_WithStructArray_ShouldBeAsExpected(t *testing.T) {
|
||||
// Arrange
|
||||
arr := []struct{ d float64 }{{d: 0.1}, {d: 1.5}, {d: 0.4}, {d: 2.5}, {d: 5.521}}
|
||||
// Act
|
||||
r := Sum(arr, func(val struct{ d float64 }) float64 {
|
||||
return val.d
|
||||
})
|
||||
// Assert
|
||||
const expected = 10.021
|
||||
assert.True(t, r == expected)
|
||||
}
|
||||
39
pkg/array/any.go
Normal file
39
pkg/array/any.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package array
|
||||
|
||||
func All[T any](arr []T, predicate func(val T) bool) bool {
|
||||
for i := 0; i < len(arr); i++ {
|
||||
if !predicate(arr[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Any returns true if any element in the array satisfies the predicate; otherwise, it returns false.
|
||||
func Any[TIn any](arr []TIn, predicate func(val TIn) bool) bool {
|
||||
for i := 0; i < len(arr); i++ {
|
||||
if predicate(arr[i]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AnyError[TIn any](arr []TIn, predicate func(val TIn) error) error {
|
||||
for i := 0; i < len(arr); i++ {
|
||||
if err := predicate(arr[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Contains checks if a slice contains a specific element.
|
||||
func Contains[T comparable](slice []T, element T) bool {
|
||||
for i := 0; i < len(slice); i++ {
|
||||
if slice[i] == element {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
75
pkg/array/diff.go
Normal file
75
pkg/array/diff.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package array
|
||||
|
||||
func Diff[T comparable](slice1, slice2 []T) []T {
|
||||
var result []T
|
||||
|
||||
elementsMap := make(map[T]bool)
|
||||
for _, v := range slice2 {
|
||||
elementsMap[v] = true
|
||||
}
|
||||
|
||||
for _, v := range slice1 {
|
||||
if !elementsMap[v] {
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func MapDiff[T comparable](slice1, slice2 []T) []T {
|
||||
var result []T
|
||||
|
||||
arr1elementsMap := make(map[T]int)
|
||||
for _, v := range slice1 {
|
||||
arr1elementsMap[v] += 1
|
||||
}
|
||||
|
||||
arr2elementsMap := make(map[T]int)
|
||||
for _, v := range slice2 {
|
||||
arr2elementsMap[v] += 1
|
||||
}
|
||||
|
||||
for key, count1 := range arr1elementsMap {
|
||||
if count2, ok := arr2elementsMap[key]; !ok || count2 != count1 {
|
||||
result = append(result, key)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// DiffByKeyAndValue returns the elements from slice1 that do not have
|
||||
// corresponding elements in slice2 based on a key and a comparison function.
|
||||
// T1 and T2 are the types of the elements in slice1 and slice2 respectively.
|
||||
// K is the type of the key used for comparison.
|
||||
func DiffByKeyAndValue[T1 any, T2 any, K comparable](
|
||||
slice1 []T1,
|
||||
slice2 []T2,
|
||||
getKeyFromSlice1 func(T1) K,
|
||||
getKeyFromSlice2 func(T2) K,
|
||||
compare func(T1, T2) bool,
|
||||
) []T1 {
|
||||
// Create a map to index elements of slice2 by their keys
|
||||
indexedSlice2 := make(map[K]T2)
|
||||
for _, elementFromSlice2 := range slice2 {
|
||||
key := getKeyFromSlice2(elementFromSlice2)
|
||||
indexedSlice2[key] = elementFromSlice2
|
||||
}
|
||||
|
||||
// Initialize a slice to hold the elements that are different
|
||||
var differingElements []T1
|
||||
|
||||
// Iterate over slice1 and find elements that are not in slice2
|
||||
for _, elementFromSlice1 := range slice1 {
|
||||
key := getKeyFromSlice1(elementFromSlice1)
|
||||
|
||||
// Check if the key exists in the indexed slice2
|
||||
if correspondingElementFromSlice2, exists := indexedSlice2[key]; !exists || !compare(elementFromSlice1, correspondingElementFromSlice2) {
|
||||
// If it doesn't exist or the comparison fails, add to the result
|
||||
differingElements = append(differingElements, elementFromSlice1)
|
||||
}
|
||||
}
|
||||
|
||||
return differingElements
|
||||
}
|
||||
5
pkg/array/empty.go
Normal file
5
pkg/array/empty.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package array
|
||||
|
||||
func IsEmpty[TIn any](arr []TIn) bool {
|
||||
return arr == nil || len(arr) == 0
|
||||
}
|
||||
7
pkg/array/enumerator.go
Normal file
7
pkg/array/enumerator.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package array
|
||||
|
||||
type Enumerator[T any] interface {
|
||||
Next() bool
|
||||
Current() (*T, error)
|
||||
Destroy() error
|
||||
}
|
||||
289
pkg/array/example_test.go
Normal file
289
pkg/array/example_test.go
Normal file
@@ -0,0 +1,289 @@
|
||||
package array_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"base/pkg/array"
|
||||
)
|
||||
|
||||
// Product represents a product in an base system
|
||||
type Product struct {
|
||||
ID int
|
||||
Name string
|
||||
Price float64
|
||||
Category string
|
||||
}
|
||||
|
||||
// ProductDTO is a data transfer object for Product
|
||||
type ProductDTO struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
PriceUSD string `json:"price_usd"`
|
||||
Available bool `json:"available"`
|
||||
}
|
||||
|
||||
// base represents a store's base
|
||||
type base struct {
|
||||
StoreID int
|
||||
StoreName string
|
||||
Products []Product
|
||||
}
|
||||
|
||||
// Review represents a customer review
|
||||
type Review struct {
|
||||
ProductID int
|
||||
Rating int
|
||||
Comment string
|
||||
}
|
||||
|
||||
func Example_map() {
|
||||
// Create a slice of Product structs
|
||||
products := []Product{
|
||||
{ID: 1, Name: "Laptop", Price: 999.99, Category: "Electronics"},
|
||||
{ID: 2, Name: "Headphones", Price: 99.99, Category: "Electronics"},
|
||||
{ID: 3, Name: "Keyboard", Price: 49.99, Category: "Accessories"},
|
||||
}
|
||||
|
||||
// Use Map to transform Product structs to ProductDTO structs
|
||||
productDTOs := array.Map(products, func(p Product, i int) ProductDTO {
|
||||
return ProductDTO{
|
||||
ID: p.ID,
|
||||
Name: p.Name,
|
||||
PriceUSD: fmt.Sprintf("$%.2f", p.Price),
|
||||
Available: p.Price > 0,
|
||||
}
|
||||
})
|
||||
|
||||
// Print the result
|
||||
for _, dto := range productDTOs {
|
||||
fmt.Printf("Product %d: %s - %s\n", dto.ID, dto.Name, dto.PriceUSD)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Product 1: Laptop - $999.99
|
||||
// Product 2: Headphones - $99.99
|
||||
// Product 3: Keyboard - $49.99
|
||||
}
|
||||
|
||||
func Example_mapWithError() {
|
||||
// Create a slice of Product structs
|
||||
products := []Product{
|
||||
{ID: 1, Name: "Laptop", Price: 999.99, Category: "Electronics"},
|
||||
{ID: 2, Name: "Headphones", Price: 99.99, Category: "Electronics"},
|
||||
{ID: 3, Name: "Keyboard", Price: 49.99, Category: "Accessories"},
|
||||
}
|
||||
|
||||
// Use MapWithError to transform Product structs to discounted products,
|
||||
// but only if the discount can be applied
|
||||
discountedProducts, err := array.MapWithError(products, func(p Product, i int) (*Product, error) {
|
||||
// For this example, we'll say we can't discount items under $50
|
||||
if p.Price < 50.0 {
|
||||
return nil, errors.New("cannot discount items under $50")
|
||||
}
|
||||
|
||||
// Create a new product with 10% discount
|
||||
discounted := p
|
||||
discounted.Price = p.Price * 0.9
|
||||
return &discounted, nil
|
||||
})
|
||||
|
||||
// Check for errors
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
} else {
|
||||
// Print the result
|
||||
for _, p := range discountedProducts {
|
||||
fmt.Printf("Discounted %s: $%.2f\n", p.Name, p.Price)
|
||||
}
|
||||
}
|
||||
|
||||
// Try with products that all meet the criteria
|
||||
expensiveProducts := []Product{
|
||||
{ID: 1, Name: "Laptop", Price: 999.99, Category: "Electronics"},
|
||||
{ID: 2, Name: "Smartphone", Price: 699.99, Category: "Electronics"},
|
||||
}
|
||||
|
||||
discountedProducts, err = array.MapWithError(expensiveProducts, func(p Product, i int) (*Product, error) {
|
||||
// All these products can be discounted
|
||||
discounted := p
|
||||
discounted.Price = p.Price * 0.9
|
||||
return &discounted, nil
|
||||
})
|
||||
|
||||
// Print the successful result
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
} else {
|
||||
for _, p := range discountedProducts {
|
||||
fmt.Printf("Discounted %s: $%.2f\n", p.Name, p.Price)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Error: cannot discount items under $50
|
||||
// Discounted Laptop: $899.99
|
||||
// Discounted Smartphone: $629.99
|
||||
}
|
||||
|
||||
func Example_mapD() {
|
||||
// Create a map of store inventories
|
||||
storeInventories := map[string]base{
|
||||
"NY": {
|
||||
StoreID: 1,
|
||||
StoreName: "New York Store",
|
||||
Products: []Product{
|
||||
{ID: 1, Name: "Laptop", Price: 999.99, Category: "Electronics"},
|
||||
{ID: 2, Name: "Headphones", Price: 99.99, Category: "Electronics"},
|
||||
},
|
||||
},
|
||||
"LA": {
|
||||
StoreID: 2,
|
||||
StoreName: "Los Angeles Store",
|
||||
Products: []Product{
|
||||
{ID: 1, Name: "Laptop", Price: 1099.99, Category: "Electronics"},
|
||||
{ID: 3, Name: "Keyboard", Price: 49.99, Category: "Accessories"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Use MapD to extract and format store information
|
||||
storeInfos := array.MapD(storeInventories, func(inv base, location string) string {
|
||||
return fmt.Sprintf("%s (ID: %d) - %s - %d products",
|
||||
inv.StoreName, inv.StoreID, location, len(inv.Products))
|
||||
})
|
||||
|
||||
// Sort the results for consistent output
|
||||
sort.Strings(storeInfos)
|
||||
|
||||
// Print the result
|
||||
for _, info := range storeInfos {
|
||||
fmt.Println(info)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Los Angeles Store (ID: 2) - LA - 2 products
|
||||
// New York Store (ID: 1) - NY - 2 products
|
||||
}
|
||||
|
||||
func Example_forEach() {
|
||||
// Create a slice of Product structs
|
||||
products := []Product{
|
||||
{ID: 1, Name: "Laptop", Price: 999.99, Category: "Electronics"},
|
||||
{ID: 2, Name: "Headphones", Price: 99.99, Category: "Electronics"},
|
||||
{ID: 3, Name: "Keyboard", Price: 49.99, Category: "Accessories"},
|
||||
}
|
||||
|
||||
// Use ForEach to apply a 10% discount to all products
|
||||
array.ForEach(products, func(p *Product, i int) {
|
||||
p.Price = p.Price * 0.9
|
||||
})
|
||||
|
||||
// Print the result
|
||||
for _, p := range products {
|
||||
fmt.Printf("%s: $%.2f\n", p.Name, p.Price)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Laptop: $899.99
|
||||
// Headphones: $89.99
|
||||
// Keyboard: $44.99
|
||||
}
|
||||
|
||||
func Example_mapMany() {
|
||||
// Create a slice of base structs
|
||||
stores := []base{
|
||||
{
|
||||
StoreID: 1,
|
||||
StoreName: "New York Store",
|
||||
Products: []Product{
|
||||
{ID: 1, Name: "Laptop", Price: 999.99, Category: "Electronics"},
|
||||
{ID: 2, Name: "Headphones", Price: 99.99, Category: "Electronics"},
|
||||
},
|
||||
},
|
||||
{
|
||||
StoreID: 2,
|
||||
StoreName: "Los Angeles Store",
|
||||
Products: []Product{
|
||||
{ID: 1, Name: "Laptop", Price: 1099.99, Category: "Electronics"},
|
||||
{ID: 3, Name: "Keyboard", Price: 49.99, Category: "Accessories"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Use MapMany to flatten the store inventories into a list of product information
|
||||
// but only include products priced over $100
|
||||
productInfos := array.MapMany(stores,
|
||||
func(store base) []Product {
|
||||
return store.Products
|
||||
},
|
||||
func(store base, product Product) *string {
|
||||
if product.Price < 100 {
|
||||
return nil // Skip products under $100
|
||||
}
|
||||
info := fmt.Sprintf("%s - %s - $%.2f",
|
||||
store.StoreName, product.Name, product.Price)
|
||||
return &info
|
||||
})
|
||||
|
||||
// Sort for consistent output
|
||||
sort.Strings(productInfos)
|
||||
|
||||
// Print the result
|
||||
for _, info := range productInfos {
|
||||
fmt.Println(info)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Los Angeles Store - Laptop - $1099.99
|
||||
// New York Store - Laptop - $999.99
|
||||
}
|
||||
|
||||
func Example_mapManyD() {
|
||||
// Create a map of store inventories
|
||||
storeInventories := map[string]base{
|
||||
"NY": {
|
||||
StoreID: 1,
|
||||
StoreName: "New York Store",
|
||||
Products: []Product{
|
||||
{ID: 1, Name: "Laptop", Price: 999.99, Category: "Electronics"},
|
||||
{ID: 2, Name: "Headphones", Price: 99.99, Category: "Electronics"},
|
||||
},
|
||||
},
|
||||
"LA": {
|
||||
StoreID: 2,
|
||||
StoreName: "Los Angeles Store",
|
||||
Products: []Product{
|
||||
{ID: 1, Name: "Laptop", Price: 1099.99, Category: "Electronics"},
|
||||
{ID: 3, Name: "Keyboard", Price: 49.99, Category: "Accessories"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Use MapManyD to flatten the store inventories into a list of product names
|
||||
productNames := array.MapManyD(storeInventories,
|
||||
func(base base) []Product {
|
||||
return base.Products
|
||||
},
|
||||
func(product Product) string {
|
||||
return strings.ToUpper(product.Name)
|
||||
})
|
||||
|
||||
// Sort for consistent output
|
||||
sort.Strings(productNames)
|
||||
|
||||
// Print the result
|
||||
fmt.Println("All product names (uppercase):")
|
||||
for _, name := range productNames {
|
||||
fmt.Println(name)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// All product names (uppercase):
|
||||
// HEADPHONES
|
||||
// KEYBOARD
|
||||
// LAPTOP
|
||||
// LAPTOP
|
||||
}
|
||||
20
pkg/array/find.go
Normal file
20
pkg/array/find.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package array
|
||||
|
||||
func Find[TIn any](arr []TIn, predicate func(val TIn) bool) *TIn {
|
||||
for i := range arr {
|
||||
if predicate(arr[i]) {
|
||||
return &arr[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Filter[TIn any](arr []TIn, predicate func(val *TIn) bool) []TIn {
|
||||
var r []TIn
|
||||
for i := range arr {
|
||||
if predicate(&arr[i]) {
|
||||
r = append(r, arr[i])
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
188
pkg/array/map.go
Normal file
188
pkg/array/map.go
Normal file
@@ -0,0 +1,188 @@
|
||||
package array
|
||||
|
||||
// MapWithError transforms each element in the input slice to a new type, with error handling.
|
||||
//
|
||||
// It applies the selector function to each element in the input slice and its index.
|
||||
// If the selector function returns an error for any element, the function immediately
|
||||
// returns that error and a nil slice. Otherwise, it returns a new slice containing
|
||||
// all transformed elements and nil error.
|
||||
//
|
||||
// Generic parameters:
|
||||
// - TIn: The type of elements in the input slice
|
||||
// - TOut: The type of elements in the output slice
|
||||
//
|
||||
// Parameters:
|
||||
// - arr: The input slice to transform
|
||||
// - selector: A function that takes an element and its index, returning a pointer to
|
||||
// the transformed value and an error
|
||||
//
|
||||
// Returns:
|
||||
// - A slice of transformed elements
|
||||
// - An error if the transformation failed for any element
|
||||
func MapWithError[TIn any, TOut any](arr []TIn, selector func(val TIn, index int) (*TOut, error)) ([]TOut, error) {
|
||||
var output []TOut
|
||||
for i := range arr {
|
||||
out, err := selector(arr[i], i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
output = append(output, *out)
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// Map transforms each element in the input slice to a new type.
|
||||
//
|
||||
// It applies the selector function to each element in the input slice and its index,
|
||||
// returning a new slice containing all transformed elements.
|
||||
//
|
||||
// Generic parameters:
|
||||
// - TIn: The type of elements in the input slice
|
||||
// - TOut: The type of elements in the output slice
|
||||
//
|
||||
// Parameters:
|
||||
// - arr: The input slice to transform
|
||||
// - selector: A function that takes an element and its index, returning the transformed value
|
||||
//
|
||||
// Returns:
|
||||
// - A slice of transformed elements
|
||||
func Map[TIn any, TOut any](arr []TIn, selector func(val TIn, index int) TOut) []TOut {
|
||||
var output []TOut
|
||||
for i := range arr {
|
||||
out := selector(arr[i], i)
|
||||
output = append(output, out)
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// MapD transforms each value in a map to an element in a slice.
|
||||
//
|
||||
// It applies the selector function to each value and key in the input map,
|
||||
// returning a slice containing all transformed values.
|
||||
//
|
||||
// Generic parameters:
|
||||
// - TKey: The type of keys in the input map (must be comparable)
|
||||
// - TIn: The type of values in the input map
|
||||
// - TOut: The type of elements in the output slice
|
||||
//
|
||||
// Parameters:
|
||||
// - m: The input map to transform
|
||||
// - selector: A function that takes a value and its key, returning the transformed value
|
||||
//
|
||||
// Returns:
|
||||
// - A slice of transformed values
|
||||
func MapD[TKey comparable, TIn any, TOut any](m map[TKey]TIn, selector func(val TIn, key TKey) TOut) []TOut {
|
||||
var output []TOut
|
||||
for i := range m {
|
||||
out := selector(m[i], i)
|
||||
output = append(output, out)
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// ForEach applies a function to each element in the input slice.
|
||||
//
|
||||
// Unlike Map, ForEach modifies elements in place by providing a pointer to each element.
|
||||
// This function does not return a new slice.
|
||||
//
|
||||
// Generic parameters:
|
||||
// - TIn: The type of elements in the input slice
|
||||
//
|
||||
// Parameters:
|
||||
// - arr: The input slice whose elements will be processed
|
||||
// - selector: A function that takes a pointer to an element and its index
|
||||
func ForEach[TIn any](arr []TIn, selector func(val *TIn, index int)) {
|
||||
for i := 0; i < len(arr); i++ {
|
||||
selector(&arr[i], i)
|
||||
}
|
||||
}
|
||||
|
||||
// MapMany transforms and flattens a nested collection structure.
|
||||
//
|
||||
// It first applies the collectionSelector to each element in the input slice to produce
|
||||
// an inner collection. Then it applies the resultSelector to each inner element along with
|
||||
// the original element, flattening the result into a single output slice. If resultSelector
|
||||
// returns nil for any element, that element is skipped in the output.
|
||||
//
|
||||
// Generic parameters:
|
||||
// - TIn: The type of elements in the input slice
|
||||
// - TC: The type of elements in the inner collections
|
||||
// - TOut: The type of elements in the output slice
|
||||
//
|
||||
// Parameters:
|
||||
// - m: The input slice to transform
|
||||
// - collectionSelector: A function that produces an inner collection from each input element
|
||||
// - resultSelector: A function that transforms each inner element along with its parent element
|
||||
//
|
||||
// Returns:
|
||||
// - A flattened slice of transformed elements
|
||||
func MapMany[TIn any, TC any, TOut any](m []TIn, collectionSelector func(TIn) []TC, resultSelector func(TIn, TC) *TOut) []TOut {
|
||||
var output []TOut
|
||||
|
||||
for i := range m {
|
||||
out := collectionSelector(m[i])
|
||||
for _, v := range out {
|
||||
result := resultSelector(m[i], v)
|
||||
if result == nil {
|
||||
continue
|
||||
}
|
||||
output = append(output, *result)
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// MapManyD transforms and flattens values from a map.
|
||||
//
|
||||
// It first applies the collectionSelector to each value in the input map to produce
|
||||
// an inner collection. Then it applies the resultSelector to each inner element,
|
||||
// flattening the results into a single output slice.
|
||||
//
|
||||
// Generic parameters:
|
||||
// - TKey: The type of keys in the input map (must be comparable)
|
||||
// - TIn: The type of values in the input map
|
||||
// - TC: The type of elements in the inner collections
|
||||
// - TOut: The type of elements in the output slice
|
||||
//
|
||||
// Parameters:
|
||||
// - m: The input map to transform
|
||||
// - collectionSelector: A function that produces an inner collection from each input value
|
||||
// - resultSelector: A function that transforms each inner element
|
||||
//
|
||||
// Returns:
|
||||
// - A flattened slice of transformed elements
|
||||
func MapManyD[TKey comparable, TIn any, TC any, TOut any](m map[TKey]TIn, collectionSelector func(TIn) []TC, resultSelector func(TC) TOut) []TOut {
|
||||
var output []TOut
|
||||
|
||||
for i := range m {
|
||||
out := collectionSelector(m[i])
|
||||
for _, v := range out {
|
||||
output = append(output, resultSelector(v))
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// ToMap converts a slice of items into a map using the provided key and value selectors.
|
||||
// TKey is the type of the keys in the resulting map, TIn is the type of items in the input slice,
|
||||
// and TOut is the type of the values in the resulting map.
|
||||
func ToMap[TKey comparable, TIn any, TOut any](
|
||||
items []TIn,
|
||||
keySelector func(TIn) TKey,
|
||||
valueSelector func(TIn) TOut,
|
||||
) map[TKey]TOut {
|
||||
// Create a map with an initial capacity equal to the length of the input slice
|
||||
resultMap := make(map[TKey]TOut, len(items))
|
||||
|
||||
// Iterate through each item in the slice
|
||||
for _, item := range items {
|
||||
// Get the key and value using the provided selectors
|
||||
key := keySelector(item)
|
||||
value := valueSelector(item)
|
||||
|
||||
// Store the key-value pair in the result map
|
||||
resultMap[key] = value
|
||||
}
|
||||
|
||||
return resultMap
|
||||
}
|
||||
362
pkg/array/map_test.go
Normal file
362
pkg/array/map_test.go
Normal file
@@ -0,0 +1,362 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMapWithError(t *testing.T) {
|
||||
t.Run("success case", func(t *testing.T) {
|
||||
// Arrange
|
||||
input := []int{1, 2, 3}
|
||||
expected := []string{"1", "2", "3"}
|
||||
|
||||
// Act
|
||||
result, err := MapWithError(input, func(val int, index int) (*string, error) {
|
||||
str := string(rune(val + '0'))
|
||||
return &str, nil
|
||||
})
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("Expected %v, got %v", expected, result)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("error case", func(t *testing.T) {
|
||||
// Arrange
|
||||
input := []int{1, 2, 3}
|
||||
testErr := errors.New("test error")
|
||||
|
||||
// Act
|
||||
result, err := MapWithError(input, func(val int, index int) (*string, error) {
|
||||
if val == 2 {
|
||||
return nil, testErr
|
||||
}
|
||||
str := string(rune(val + '0'))
|
||||
return &str, nil
|
||||
})
|
||||
|
||||
// Assert
|
||||
if err != testErr {
|
||||
t.Errorf("Expected error %v, got %v", testErr, err)
|
||||
}
|
||||
if result != nil {
|
||||
t.Errorf("Expected nil result, got %v", result)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty array", func(t *testing.T) {
|
||||
// Arrange
|
||||
var input []int
|
||||
|
||||
// Act
|
||||
result, err := MapWithError(input, func(val int, index int) (*string, error) {
|
||||
str := string(rune(val + '0'))
|
||||
return &str, nil
|
||||
})
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %v", err)
|
||||
}
|
||||
if len(result) != 0 {
|
||||
t.Errorf("Expected empty result, got %v", result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
t.Run("basic transformation", func(t *testing.T) {
|
||||
// Arrange
|
||||
input := []int{1, 2, 3}
|
||||
expected := []string{"1", "2", "3"}
|
||||
|
||||
// Act
|
||||
result := Map(input, func(val int, index int) string {
|
||||
return string(rune(val + '0'))
|
||||
})
|
||||
|
||||
// Assert
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("Expected %v, got %v", expected, result)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("use index in transformation", func(t *testing.T) {
|
||||
// Arrange
|
||||
input := []string{"a", "b", "c"}
|
||||
expected := []string{"a0", "b1", "c2"}
|
||||
|
||||
// Act
|
||||
result := Map(input, func(val string, index int) string {
|
||||
return val + string(rune(index+'0'))
|
||||
})
|
||||
|
||||
// Assert
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("Expected %v, got %v", expected, result)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty array", func(t *testing.T) {
|
||||
// Arrange
|
||||
var input []int
|
||||
|
||||
// Act
|
||||
result := Map(input, func(val int, index int) string {
|
||||
return string(rune(val + '0'))
|
||||
})
|
||||
|
||||
// Assert
|
||||
if len(result) != 0 {
|
||||
t.Errorf("Expected empty result, got %v", result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestMapD(t *testing.T) {
|
||||
t.Run("map dictionary to array", func(t *testing.T) {
|
||||
// Arrange
|
||||
input := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
}
|
||||
|
||||
// Act
|
||||
result := MapD(input, func(val int, key string) string {
|
||||
return key + string(rune(val+'0'))
|
||||
})
|
||||
|
||||
// Assert
|
||||
// Since map iteration order is not guaranteed, we check that all expected elements are in the result
|
||||
expectedElements := []string{"a1", "b2", "c3"}
|
||||
if len(result) != len(expectedElements) {
|
||||
t.Errorf("Expected result length %d, got %d", len(expectedElements), len(result))
|
||||
}
|
||||
|
||||
resultMap := make(map[string]bool)
|
||||
for _, v := range result {
|
||||
resultMap[v] = true
|
||||
}
|
||||
|
||||
for _, expected := range expectedElements {
|
||||
if !resultMap[expected] {
|
||||
t.Errorf("Expected result to contain %s, but it doesn't", expected)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty map", func(t *testing.T) {
|
||||
// Arrange
|
||||
input := map[string]int{}
|
||||
|
||||
// Act
|
||||
result := MapD(input, func(val int, key string) string {
|
||||
return key + string(rune(val+'0'))
|
||||
})
|
||||
|
||||
// Assert
|
||||
if len(result) != 0 {
|
||||
t.Errorf("Expected empty result, got %v", result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestForEach(t *testing.T) {
|
||||
t.Run("modify array in place", func(t *testing.T) {
|
||||
// Arrange
|
||||
input := []int{1, 2, 3}
|
||||
expected := []int{2, 3, 4}
|
||||
|
||||
// Act
|
||||
ForEach(input, func(val *int, index int) {
|
||||
*val += 1
|
||||
})
|
||||
|
||||
// Assert
|
||||
if !reflect.DeepEqual(input, expected) {
|
||||
t.Errorf("Expected %v, got %v", expected, input)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("use index in modification", func(t *testing.T) {
|
||||
// Arrange
|
||||
input := []int{1, 2, 3}
|
||||
expected := []int{1, 3, 5}
|
||||
|
||||
// Act
|
||||
ForEach(input, func(val *int, index int) {
|
||||
*val = *val + index
|
||||
})
|
||||
|
||||
// Assert
|
||||
if !reflect.DeepEqual(input, expected) {
|
||||
t.Errorf("Expected %v, got %v", expected, input)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty array", func(t *testing.T) {
|
||||
// Arrange
|
||||
var input []int
|
||||
callCount := 0
|
||||
|
||||
// Act
|
||||
ForEach(input, func(val *int, index int) {
|
||||
callCount++
|
||||
})
|
||||
|
||||
// Assert
|
||||
if callCount != 0 {
|
||||
t.Errorf("Expected callback not to be called, but it was called %d times", callCount)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestMapMany(t *testing.T) {
|
||||
t.Run("basic flat mapping", func(t *testing.T) {
|
||||
// Arrange
|
||||
input := []int{1, 2}
|
||||
expected := []string{"1a", "1b", "2a", "2b"}
|
||||
|
||||
// Act
|
||||
result := MapMany(input,
|
||||
func(i int) []string {
|
||||
return []string{"a", "b"}
|
||||
},
|
||||
func(i int, s string) *string {
|
||||
res := string(rune(i+'0')) + s
|
||||
return &res
|
||||
})
|
||||
|
||||
// Assert
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("Expected %v, got %v", expected, result)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with nil results", func(t *testing.T) {
|
||||
// Arrange
|
||||
input := []int{1, 2, 3}
|
||||
expected := []string{"1a", "2a", "3a"}
|
||||
|
||||
// Act
|
||||
result := MapMany(input,
|
||||
func(i int) []string {
|
||||
return []string{"a", "b"}
|
||||
},
|
||||
func(i int, s string) *string {
|
||||
if s == "b" {
|
||||
return nil
|
||||
}
|
||||
res := string(rune(i+'0')) + s
|
||||
return &res
|
||||
})
|
||||
|
||||
// Assert
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("Expected %v, got %v", expected, result)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty input array", func(t *testing.T) {
|
||||
// Arrange
|
||||
var input []int
|
||||
|
||||
// Act
|
||||
result := MapMany(input,
|
||||
func(i int) []string {
|
||||
return []string{"a", "b"}
|
||||
},
|
||||
func(i int, s string) *string {
|
||||
res := string(rune(i+'0')) + s
|
||||
return &res
|
||||
})
|
||||
|
||||
// Assert
|
||||
if len(result) != 0 {
|
||||
t.Errorf("Expected empty result, got %v", result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestMapManyD(t *testing.T) {
|
||||
t.Run("map dictionary to flattened array", func(t *testing.T) {
|
||||
// Arrange
|
||||
input := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
}
|
||||
|
||||
// Act
|
||||
result := MapManyD(input,
|
||||
func(val int) []string {
|
||||
return []string{"x", "y"}
|
||||
},
|
||||
func(s string) string {
|
||||
return s + "z"
|
||||
})
|
||||
|
||||
// Assert
|
||||
// Since map iteration order is not guaranteed, we check that all expected elements are in the result
|
||||
expectedElements := []string{"xz", "yz", "xz", "yz"}
|
||||
if len(result) != len(expectedElements) {
|
||||
t.Errorf("Expected result length %d, got %d", len(expectedElements), len(result))
|
||||
}
|
||||
|
||||
resultMap := make(map[string]int)
|
||||
for _, v := range result {
|
||||
resultMap[v]++
|
||||
}
|
||||
|
||||
if resultMap["xz"] != 2 || resultMap["yz"] != 2 {
|
||||
t.Errorf("Expected result to contain 2 of each 'xz' and 'yz', got %v", resultMap)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty inner collection", func(t *testing.T) {
|
||||
// Arrange
|
||||
input := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
}
|
||||
|
||||
// Act
|
||||
result := MapManyD(input,
|
||||
func(val int) []string {
|
||||
return []string{}
|
||||
},
|
||||
func(s string) string {
|
||||
return s + "z"
|
||||
})
|
||||
|
||||
// Assert
|
||||
if len(result) != 0 {
|
||||
t.Errorf("Expected empty result, got %v", result)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty input map", func(t *testing.T) {
|
||||
// Arrange
|
||||
input := map[string]int{}
|
||||
|
||||
// Act
|
||||
result := MapManyD(input,
|
||||
func(val int) []string {
|
||||
return []string{"x", "y"}
|
||||
},
|
||||
func(s string) string {
|
||||
return s + "z"
|
||||
})
|
||||
|
||||
// Assert
|
||||
if len(result) != 0 {
|
||||
t.Errorf("Expected empty result, got %v", result)
|
||||
}
|
||||
})
|
||||
}
|
||||
72
pkg/array/sort.go
Normal file
72
pkg/array/sort.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package array
|
||||
|
||||
import "sort"
|
||||
|
||||
type Numbers interface {
|
||||
int | int8 | int16 | int32 | int64 | float32 | float64
|
||||
}
|
||||
|
||||
// BubbleSort
|
||||
// Deprecated; use sort package
|
||||
func BubbleSort[T any, N Numbers](arr []T, selector func(val T) N) {
|
||||
n := len(arr)
|
||||
|
||||
for i := 0; i < n-1; i++ {
|
||||
for j := 0; j < n-i-1; j++ {
|
||||
c := selector(arr[j])
|
||||
n := selector(arr[j+1])
|
||||
|
||||
if c > n {
|
||||
// swap arr[j] and arr[j+1]
|
||||
arr[j], arr[j+1] = arr[j+1], arr[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BubbleSortDesc
|
||||
// Deprecated; use sort package
|
||||
func BubbleSortDesc[T any](arr []T, selector func(val T) float64) {
|
||||
n := len(arr)
|
||||
|
||||
for i := 0; i < n-1; i++ {
|
||||
for j := 0; j < n-i-1; j++ {
|
||||
c := selector(arr[j])
|
||||
n := selector(arr[j+1])
|
||||
|
||||
if c < n { // Change comparison operator to less than
|
||||
// swap arr[j] and arr[j+1]
|
||||
arr[j], arr[j+1] = arr[j+1], arr[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func FindDifference[T Numbers](primary, secondary []T) []T {
|
||||
m := make(map[T]struct{})
|
||||
for _, num := range secondary {
|
||||
m[num] = struct{}{}
|
||||
}
|
||||
|
||||
var diff []T
|
||||
for _, num := range primary {
|
||||
if _, found := m[num]; !found {
|
||||
diff = append(diff, num)
|
||||
}
|
||||
}
|
||||
|
||||
return diff
|
||||
}
|
||||
|
||||
func SortIntMap[T any](m map[int]T) []T {
|
||||
result := make([]T, 0, len(m))
|
||||
keys := make([]int, 0, len(m))
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Ints(keys)
|
||||
for _, k := range keys {
|
||||
result = append(result, m[k])
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user