mirror of
https://github.com/IBM/fp-go.git
synced 2025-08-10 22:31:32 +02:00
fix: implement FoldMap
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
@@ -16,11 +16,14 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
G "github.com/IBM/fp-go/internal/record"
|
||||
Mg "github.com/IBM/fp-go/magma"
|
||||
Mo "github.com/IBM/fp-go/monoid"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
"github.com/IBM/fp-go/ord"
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
)
|
||||
|
||||
@@ -40,6 +43,46 @@ func Values[M ~map[K]V, GV ~[]V, K comparable, V any](r M) GV {
|
||||
return collect[M, GV](r, F.Second[K, V])
|
||||
}
|
||||
|
||||
func KeysOrd[M ~map[K]V, GK ~[]K, K comparable, V any](o ord.Ord[K]) func(r M) GK {
|
||||
return func(r M) GK {
|
||||
return collectOrd[M, GK](o, r, F.First[K, V])
|
||||
}
|
||||
}
|
||||
|
||||
func ValuesOrd[M ~map[K]V, GV ~[]V, K comparable, V any](o ord.Ord[K]) func(r M) GV {
|
||||
return func(r M) GV {
|
||||
return collectOrd[M, GV](o, r, F.Second[K, V])
|
||||
}
|
||||
}
|
||||
|
||||
func collectOrd[M ~map[K]V, GR ~[]R, K comparable, V, R any](o ord.Ord[K], r M, f func(K, V) R) GR {
|
||||
// create the entries
|
||||
entries := toEntriesOrd[M, []T.Tuple2[K, V]](o, r)
|
||||
// collect this array
|
||||
ft := T.Tupled2(f)
|
||||
count := len(entries)
|
||||
result := make(GR, count)
|
||||
for i := count - 1; i >= 0; i-- {
|
||||
result[i] = ft(entries[i])
|
||||
}
|
||||
// done
|
||||
return result
|
||||
}
|
||||
|
||||
func reduceOrd[M ~map[K]V, K comparable, V, R any](o ord.Ord[K], r M, f func(K, R, V) R, initial R) R {
|
||||
// create the entries
|
||||
entries := toEntriesOrd[M, []T.Tuple2[K, V]](o, r)
|
||||
// collect this array
|
||||
current := initial
|
||||
count := len(entries)
|
||||
for i := 0; i < count; i++ {
|
||||
t := entries[i]
|
||||
current = f(T.First(t), current, T.Second(t))
|
||||
}
|
||||
// done
|
||||
return current
|
||||
}
|
||||
|
||||
func collect[M ~map[K]V, GR ~[]R, K comparable, V, R any](r M, f func(K, V) R) GR {
|
||||
count := len(r)
|
||||
result := make(GR, count)
|
||||
@@ -250,6 +293,27 @@ func ToArray[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](r M) GT {
|
||||
return collect[M, GT](r, T.MakeTuple2[K, V])
|
||||
}
|
||||
|
||||
func toEntriesOrd[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](o ord.Ord[K], r M) GT {
|
||||
// total number of elements
|
||||
count := len(r)
|
||||
// produce an array that we can sort by key
|
||||
entries := make(GT, count)
|
||||
idx := 0
|
||||
for k, v := range r {
|
||||
entries[idx] = T.MakeTuple2(k, v)
|
||||
idx++
|
||||
}
|
||||
sort.Slice(entries, func(i, j int) bool {
|
||||
return o.Compare(T.First(entries[i]), T.First(entries[j])) < 0
|
||||
})
|
||||
// final entries
|
||||
return entries
|
||||
}
|
||||
|
||||
func ToEntriesOrd[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](o ord.Ord[K]) func(r M) GT {
|
||||
return F.Bind1st(toEntriesOrd[M, GT, K, V], o)
|
||||
}
|
||||
|
||||
func ToEntries[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](r M) GT {
|
||||
return ToArray[M, GT](r)
|
||||
}
|
||||
@@ -379,3 +443,67 @@ func IsNonNil[M ~map[K]V, K comparable, V any](m M) bool {
|
||||
func ConstNil[M ~map[K]V, K comparable, V any]() M {
|
||||
return (M)(nil)
|
||||
}
|
||||
|
||||
func FoldMap[AS ~map[K]A, K comparable, A, B any](m Mo.Monoid[B]) func(func(A) B) func(AS) B {
|
||||
return func(f func(A) B) func(AS) B {
|
||||
return Reduce[AS](func(cur B, a A) B {
|
||||
return m.Concat(cur, f(a))
|
||||
}, m.Empty())
|
||||
}
|
||||
}
|
||||
|
||||
func Fold[AS ~map[K]A, K comparable, A any](m Mo.Monoid[A]) func(AS) A {
|
||||
return Reduce[AS](m.Concat, m.Empty())
|
||||
}
|
||||
|
||||
func FoldMapWithIndex[AS ~map[K]A, K comparable, A, B any](m Mo.Monoid[B]) func(func(K, A) B) func(AS) B {
|
||||
return func(f func(K, A) B) func(AS) B {
|
||||
return ReduceWithIndex[AS](func(k K, cur B, a A) B {
|
||||
return m.Concat(cur, f(k, a))
|
||||
}, m.Empty())
|
||||
}
|
||||
}
|
||||
|
||||
func ReduceOrdWithIndex[M ~map[K]V, K comparable, V, R any](o ord.Ord[K]) func(func(K, R, V) R, R) func(M) R {
|
||||
return func(f func(K, R, V) R, initial R) func(M) R {
|
||||
return func(m M) R {
|
||||
return reduceOrd(o, m, f, initial)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ReduceOrd[M ~map[K]V, K comparable, V, R any](o ord.Ord[K]) func(func(R, V) R, R) func(M) R {
|
||||
ro := ReduceOrdWithIndex[M, K, V, R](o)
|
||||
return func(f func(R, V) R, initial R) func(M) R {
|
||||
return ro(F.Ignore1of3[K](f), initial)
|
||||
}
|
||||
}
|
||||
|
||||
func FoldMapOrd[AS ~map[K]A, K comparable, A, B any](o ord.Ord[K]) func(m Mo.Monoid[B]) func(func(A) B) func(AS) B {
|
||||
red := ReduceOrd[AS, K, A, B](o)
|
||||
return func(m Mo.Monoid[B]) func(func(A) B) func(AS) B {
|
||||
return func(f func(A) B) func(AS) B {
|
||||
return red(func(cur B, a A) B {
|
||||
return m.Concat(cur, f(a))
|
||||
}, m.Empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func FoldOrd[AS ~map[K]A, K comparable, A any](o ord.Ord[K]) func(m Mo.Monoid[A]) func(AS) A {
|
||||
red := ReduceOrd[AS, K, A, A](o)
|
||||
return func(m Mo.Monoid[A]) func(AS) A {
|
||||
return red(m.Concat, m.Empty())
|
||||
}
|
||||
}
|
||||
|
||||
func FoldMapOrdWithIndex[AS ~map[K]A, K comparable, A, B any](o ord.Ord[K]) func(m Mo.Monoid[B]) func(func(K, A) B) func(AS) B {
|
||||
red := ReduceOrdWithIndex[AS, K, A, B](o)
|
||||
return func(m Mo.Monoid[B]) func(func(K, A) B) func(AS) B {
|
||||
return func(f func(K, A) B) func(AS) B {
|
||||
return red(func(k K, cur B, a A) B {
|
||||
return m.Concat(cur, f(k, a))
|
||||
}, m.Empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -19,6 +19,7 @@ import (
|
||||
Mg "github.com/IBM/fp-go/magma"
|
||||
Mo "github.com/IBM/fp-go/monoid"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
"github.com/IBM/fp-go/ord"
|
||||
G "github.com/IBM/fp-go/record/generic"
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
)
|
||||
@@ -215,3 +216,53 @@ func FilterChainWithIndex[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2]) f
|
||||
func FilterChain[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2]) func(func(V1) O.Option[map[K]V2]) func(map[K]V1) map[K]V2 {
|
||||
return G.FilterChain[map[K]V1](m)
|
||||
}
|
||||
|
||||
// FoldMap maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid.
|
||||
func FoldMap[K comparable, A, B any](m Mo.Monoid[B]) func(func(A) B) func(map[K]A) B {
|
||||
return G.FoldMap[map[K]A](m)
|
||||
}
|
||||
|
||||
// FoldMapWithIndex maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid.
|
||||
func FoldMapWithIndex[K comparable, A, B any](m Mo.Monoid[B]) func(func(K, A) B) func(map[K]A) B {
|
||||
return G.FoldMapWithIndex[map[K]A](m)
|
||||
}
|
||||
|
||||
// Fold folds the record using the provided Monoid.
|
||||
func Fold[K comparable, A any](m Mo.Monoid[A]) func(map[K]A) A {
|
||||
return G.Fold[map[K]A](m)
|
||||
}
|
||||
|
||||
// ReduceOrdWithIndex reduces a map into a single value via a reducer function making sure that the keys are passed to the reducer in the specified order
|
||||
func ReduceOrdWithIndex[V, R any, K comparable](o ord.Ord[K]) func(func(K, R, V) R, R) func(map[K]V) R {
|
||||
return G.ReduceOrdWithIndex[map[K]V, K, V, R](o)
|
||||
}
|
||||
|
||||
// ReduceOrd reduces a map into a single value via a reducer function making sure that the keys are passed to the reducer in the specified order
|
||||
func ReduceOrd[V, R any, K comparable](o ord.Ord[K]) func(func(R, V) R, R) func(map[K]V) R {
|
||||
return G.ReduceOrd[map[K]V, K, V, R](o)
|
||||
}
|
||||
|
||||
// FoldMap maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid and the items in the provided order
|
||||
func FoldMapOrd[A, B any, K comparable](o ord.Ord[K]) func(m Mo.Monoid[B]) func(func(A) B) func(map[K]A) B {
|
||||
return G.FoldMapOrd[map[K]A, K, A, B](o)
|
||||
}
|
||||
|
||||
// Fold folds the record using the provided Monoid with the items passed in the given order
|
||||
func FoldOrd[A any, K comparable](o ord.Ord[K]) func(m Mo.Monoid[A]) func(map[K]A) A {
|
||||
return G.FoldOrd[map[K]A, K, A](o)
|
||||
}
|
||||
|
||||
// FoldMapWithIndex maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid and the items in the provided order
|
||||
func FoldMapOrdWithIndex[K comparable, A, B any](o ord.Ord[K]) func(m Mo.Monoid[B]) func(func(K, A) B) func(map[K]A) B {
|
||||
return G.FoldMapOrdWithIndex[map[K]A, K, A, B](o)
|
||||
}
|
||||
|
||||
// KeysOrd returns the keys in the map in their given order
|
||||
func KeysOrd[V any, K comparable](o ord.Ord[K]) func(r map[K]V) []K {
|
||||
return G.KeysOrd[map[K]V, []K, K, V](o)
|
||||
}
|
||||
|
||||
// ValuesOrd returns the values in the map ordered by their keys in the given order
|
||||
func ValuesOrd[V any, K comparable](o ord.Ord[K]) func(r map[K]V) []V {
|
||||
return G.ValuesOrd[map[K]V, []V, K, V](o)
|
||||
}
|
||||
|
@@ -18,10 +18,12 @@ package record
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/IBM/fp-go/internal/utils"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
S "github.com/IBM/fp-go/string"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -99,3 +101,33 @@ func TestFilterChain(t *testing.T) {
|
||||
"c": "c3",
|
||||
}, res)
|
||||
}
|
||||
|
||||
func ExampleFoldMap() {
|
||||
src := map[string]string{
|
||||
"a": "a",
|
||||
"b": "b",
|
||||
"c": "c",
|
||||
}
|
||||
|
||||
fold := FoldMap[string, string](S.Monoid)(strings.ToUpper)
|
||||
|
||||
fmt.Println(fold(src))
|
||||
|
||||
// Output: ABC
|
||||
|
||||
}
|
||||
|
||||
func ExampleValuesOrd() {
|
||||
src := map[string]string{
|
||||
"c": "a",
|
||||
"b": "b",
|
||||
"a": "c",
|
||||
}
|
||||
|
||||
getValues := ValuesOrd[string](S.Ord)
|
||||
|
||||
fmt.Println(getValues(src))
|
||||
|
||||
// Output: [c b a]
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user