mirror of
https://github.com/IBM/fp-go.git
synced 2025-08-10 22:31:32 +02:00
Merge pull request #22 from IBM/cleue-add-FoldMap
fix: implement FoldMap
This commit is contained in:
@@ -294,3 +294,13 @@ func SliceRight[A any](start int) func([]A) []A {
|
||||
func Copy[A any](b []A) []A {
|
||||
return G.Copy(b)
|
||||
}
|
||||
|
||||
// FoldMap maps and folds an array. Map the Array passing each value to the iterating function. Then fold the results using the provided Monoid.
|
||||
func FoldMap[A, B any](m M.Monoid[B]) func(func(A) B) func([]A) B {
|
||||
return G.FoldMap[[]A](m)
|
||||
}
|
||||
|
||||
// Fold folds the array using the provided Monoid.
|
||||
func Fold[A any](m M.Monoid[A]) func([]A) A {
|
||||
return G.Fold[[]A](m)
|
||||
}
|
||||
|
@@ -173,3 +173,22 @@ func TestFilterMap(t *testing.T) {
|
||||
|
||||
assert.Equal(t, From("a1", "a3"), res)
|
||||
}
|
||||
|
||||
func TestFoldMap(t *testing.T) {
|
||||
src := From("a", "b", "c")
|
||||
|
||||
fold := FoldMap[string](S.Monoid)(strings.ToUpper)
|
||||
|
||||
assert.Equal(t, "ABC", fold(src))
|
||||
}
|
||||
|
||||
func ExampleFoldMap() {
|
||||
src := From("a", "b", "c")
|
||||
|
||||
fold := FoldMap[string](S.Monoid)(strings.ToUpper)
|
||||
|
||||
fmt.Println(fold(src))
|
||||
|
||||
// Output: ABC
|
||||
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@ package generic
|
||||
import (
|
||||
F "github.com/IBM/fp-go/function"
|
||||
"github.com/IBM/fp-go/internal/array"
|
||||
M "github.com/IBM/fp-go/monoid"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
"github.com/IBM/fp-go/tuple"
|
||||
)
|
||||
@@ -209,3 +210,19 @@ func Copy[AS ~[]A, A any](b AS) AS {
|
||||
copy(buf, b)
|
||||
return buf
|
||||
}
|
||||
|
||||
func FoldMap[AS ~[]A, A, B any](m M.Monoid[B]) func(func(A) B) func(AS) B {
|
||||
return func(f func(A) B) func(AS) B {
|
||||
return func(as AS) B {
|
||||
return array.Reduce(as, func(cur B, a A) B {
|
||||
return m.Concat(cur, f(a))
|
||||
}, m.Empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Fold[AS ~[]A, A any](m M.Monoid[A]) func(AS) A {
|
||||
return func(as AS) A {
|
||||
return array.Reduce(as, m.Concat, m.Empty())
|
||||
}
|
||||
}
|
||||
|
@@ -18,11 +18,17 @@ package generic
|
||||
import (
|
||||
"sort"
|
||||
|
||||
F "github.com/IBM/fp-go/function"
|
||||
O "github.com/IBM/fp-go/ord"
|
||||
)
|
||||
|
||||
// Sort implements a stable sort on the array given the provided ordering
|
||||
func Sort[GA ~[]T, T any](ord O.Ord[T]) func(ma GA) GA {
|
||||
return SortByKey[GA](ord, F.Identity[T])
|
||||
}
|
||||
|
||||
// SortByKey implements a stable sort on the array given the provided ordering on an extracted key
|
||||
func SortByKey[GA ~[]T, K, T any](ord O.Ord[K], f func(T) K) func(ma GA) GA {
|
||||
|
||||
return func(ma GA) GA {
|
||||
// nothing to sort
|
||||
@@ -34,7 +40,7 @@ func Sort[GA ~[]T, T any](ord O.Ord[T]) func(ma GA) GA {
|
||||
cpy := make(GA, l)
|
||||
copy(cpy, ma)
|
||||
sort.Slice(cpy, func(i, j int) bool {
|
||||
return ord.Compare(cpy[i], cpy[j]) < 0
|
||||
return ord.Compare(f(cpy[i]), f(cpy[j])) < 0
|
||||
})
|
||||
return cpy
|
||||
}
|
||||
|
@@ -24,3 +24,8 @@ import (
|
||||
func Sort[T any](ord O.Ord[T]) func(ma []T) []T {
|
||||
return G.Sort[[]T](ord)
|
||||
}
|
||||
|
||||
// SortByKey implements a stable sort on the array given the provided ordering on an extracted key
|
||||
func SortByKey[K, T any](ord O.Ord[K], f func(T) K) func(ma []T) []T {
|
||||
return G.SortByKey[[]T](ord, f)
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ import (
|
||||
F "github.com/IBM/fp-go/function"
|
||||
"github.com/IBM/fp-go/internal/utils"
|
||||
IO "github.com/IBM/fp-go/iooption/generic"
|
||||
M "github.com/IBM/fp-go/monoid"
|
||||
N "github.com/IBM/fp-go/number"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
@@ -226,3 +227,15 @@ func FilterChain[GVV ~func() O.Option[T.Tuple2[GVV, GV]], GV ~func() O.Option[T.
|
||||
Flatten[GVV],
|
||||
)
|
||||
}
|
||||
|
||||
func FoldMap[GU ~func() O.Option[T.Tuple2[GU, U]], FCT ~func(U) V, U, V any](m M.Monoid[V]) func(FCT) func(ma GU) V {
|
||||
return func(f FCT) func(ma GU) V {
|
||||
return Reduce[GU](func(cur V, a U) V {
|
||||
return m.Concat(cur, f(a))
|
||||
}, m.Empty())
|
||||
}
|
||||
}
|
||||
|
||||
func Fold[GU ~func() O.Option[T.Tuple2[GU, U]], U any](m M.Monoid[U]) func(ma GU) U {
|
||||
return Reduce[GU](m.Concat, m.Empty())
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@ package stateless
|
||||
import (
|
||||
G "github.com/IBM/fp-go/iterator/stateless/generic"
|
||||
L "github.com/IBM/fp-go/lazy"
|
||||
M "github.com/IBM/fp-go/monoid"
|
||||
O "github.com/IBM/fp-go/option"
|
||||
T "github.com/IBM/fp-go/tuple"
|
||||
)
|
||||
@@ -123,3 +124,13 @@ func Count(start int) Iterator[int] {
|
||||
func FilterChain[U, V any](f func(U) O.Option[Iterator[V]]) func(ma Iterator[U]) Iterator[V] {
|
||||
return G.FilterChain[Iterator[Iterator[V]], Iterator[V], Iterator[U]](f)
|
||||
}
|
||||
|
||||
// FoldMap maps and folds an iterator. Map the iterator passing each value to the iterating function. Then fold the results using the provided Monoid.
|
||||
func FoldMap[U, V any](m M.Monoid[V]) func(func(U) V) func(ma Iterator[U]) V {
|
||||
return G.FoldMap[Iterator[U], func(U) V, U, V](m)
|
||||
}
|
||||
|
||||
// Fold folds the iterator using the provided Monoid.
|
||||
func Fold[U any](m M.Monoid[U]) func(Iterator[U]) U {
|
||||
return G.Fold[Iterator[U]](m)
|
||||
}
|
||||
|
@@ -18,12 +18,14 @@ package stateless
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
A "github.com/IBM/fp-go/array"
|
||||
F "github.com/IBM/fp-go/function"
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -102,3 +104,14 @@ func TestAp(t *testing.T) {
|
||||
|
||||
assert.Equal(t, A.From("a-1-c", "a-1-d", "a-2-c", "a-2-d", "b-1-c", "b-1-d", "b-2-c", "b-2-d"), it)
|
||||
}
|
||||
|
||||
func ExampleFoldMap() {
|
||||
src := From("a", "b", "c")
|
||||
|
||||
fold := FoldMap[string](S.Monoid)(strings.ToUpper)
|
||||
|
||||
fmt.Println(fold(src))
|
||||
|
||||
// Output: ABC
|
||||
|
||||
}
|
||||
|
@@ -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