diff --git a/record/generic/record.go b/record/generic/record.go index 3c44697..55b6387 100644 --- a/record/generic/record.go +++ b/record/generic/record.go @@ -19,6 +19,7 @@ import ( "sort" F "github.com/IBM/fp-go/function" + RAG "github.com/IBM/fp-go/internal/array" FC "github.com/IBM/fp-go/internal/functor" G "github.com/IBM/fp-go/internal/record" Mg "github.com/IBM/fp-go/magma" @@ -334,6 +335,62 @@ func ToEntries[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](r M) GT { return ToArray[M, GT](r) } +// FromFoldableMap uses the reduce method for a higher kinded type to transform +// its values into a tuple. The key and value are then used to populate the map. Duplicate +// values are resolved via the provided [Mg.Magma] +func FromFoldableMap[ + FCT ~func(A) T.Tuple2[K, V], + HKTA any, + FOLDABLE ~func(func(M, A) M, M) func(HKTA) M, + M ~map[K]V, + A any, + K comparable, + V any](m Mg.Magma[V], fld FOLDABLE) func(f FCT) func(fa HKTA) M { + return func(f FCT) func(fa HKTA) M { + return fld(func(dst M, a A) M { + if IsEmpty(dst) { + dst = make(M) + } + e := f(a) + k := T.First(e) + old, ok := dst[k] + if ok { + dst[k] = m.Concat(old, T.Second(e)) + } else { + dst[k] = T.Second(e) + } + return dst + }, Empty[M]()) + } +} + +func FromFoldable[ + HKTA any, + FOLDABLE ~func(func(M, T.Tuple2[K, V]) M, M) func(HKTA) M, + M ~map[K]V, + K comparable, + V any](m Mg.Magma[V], red FOLDABLE) func(fa HKTA) M { + return FromFoldableMap[func(T.Tuple2[K, V]) T.Tuple2[K, V], HKTA, FOLDABLE](m, red)(F.Identity[T.Tuple2[K, V]]) +} + +func FromArrayMap[ + FCT ~func(A) T.Tuple2[K, V], + GA ~[]A, + M ~map[K]V, + A any, + K comparable, + V any](m Mg.Magma[V]) func(f FCT) func(fa GA) M { + return FromFoldableMap[FCT](m, F.Bind23of3(RAG.Reduce[GA, A, M])) +} + +func FromArray[ + GA ~[]T.Tuple2[K, V], + M ~map[K]V, + K comparable, + V any](m Mg.Magma[V]) func(fa GA) M { + return FromFoldable[GA](m, F.Bind23of3(RAG.Reduce[GA, T.Tuple2[K, V], M])) +} + func FromEntries[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](fa GT) M { m := make(M) for _, t := range fa { diff --git a/record/record.go b/record/record.go index 19abd71..953bd93 100644 --- a/record/record.go +++ b/record/record.go @@ -16,6 +16,7 @@ package record import ( + EM "github.com/IBM/fp-go/endomorphism" Mg "github.com/IBM/fp-go/magma" Mo "github.com/IBM/fp-go/monoid" O "github.com/IBM/fp-go/option" @@ -291,6 +292,44 @@ func Copy[K comparable, V any](m map[K]V) map[K]V { } // Clone creates a deep copy of the map using the provided endomorphism to clone the values -func Clone[K comparable, V any](f func(V) V) func(m map[K]V) map[K]V { +func Clone[K comparable, V any](f EM.Endomorphism[V]) EM.Endomorphism[map[K]V] { return G.Clone[map[K]V](f) } + +// FromFoldableMap converts from a reducer to a map +// Duplicate keys are resolved by the provided [Mg.Magma] +func FromFoldableMap[ + FOLDABLE ~func(func(map[K]V, A) map[K]V, map[K]V) func(HKTA) map[K]V, // the reduce function + A any, + HKTA any, + K comparable, + V any](m Mg.Magma[V], red FOLDABLE) func(f func(A) T.Tuple2[K, V]) func(fa HKTA) map[K]V { + return G.FromFoldableMap[func(A) T.Tuple2[K, V]](m, red) +} + +// FromArrayMap converts from an array to a map +// Duplicate keys are resolved by the provided [Mg.Magma] +func FromArrayMap[ + A any, + K comparable, + V any](m Mg.Magma[V]) func(f func(A) T.Tuple2[K, V]) func(fa []A) map[K]V { + return G.FromArrayMap[func(A) T.Tuple2[K, V], []A, map[K]V](m) +} + +// FromFoldable converts from a reducer to a map +// Duplicate keys are resolved by the provided [Mg.Magma] +func FromFoldable[ + HKTA any, + FOLDABLE ~func(func(map[K]V, T.Tuple2[K, V]) map[K]V, map[K]V) func(HKTA) map[K]V, // the reduce function + K comparable, + V any](m Mg.Magma[V], red FOLDABLE) func(fa HKTA) map[K]V { + return G.FromFoldable[HKTA, FOLDABLE](m, red) +} + +// FromArray converts from an array to a map +// Duplicate keys are resolved by the provided [Mg.Magma] +func FromArray[ + K comparable, + V any](m Mg.Magma[V]) func(fa []T.Tuple2[K, V]) map[K]V { + return G.FromArray[[]T.Tuple2[K, V], map[K]V](m) +} diff --git a/record/record_test.go b/record/record_test.go index 025a4dc..1b37322 100644 --- a/record/record_test.go +++ b/record/record_test.go @@ -1,5 +1,5 @@ -// Copyright (c) 2023 IBM Corp. // All rights reserved. +// Copyright (c) 2023 IBM Corp. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,8 +23,10 @@ import ( A "github.com/IBM/fp-go/array" "github.com/IBM/fp-go/internal/utils" + Mg "github.com/IBM/fp-go/magma" O "github.com/IBM/fp-go/option" S "github.com/IBM/fp-go/string" + T "github.com/IBM/fp-go/tuple" "github.com/stretchr/testify/assert" ) @@ -149,3 +151,28 @@ func TestCopyVsClone(t *testing.T) { assert.NotEqual(t, cpy, cln) assert.Equal(t, src, cpy) } + +func TestFromArrayMap(t *testing.T) { + src1 := A.From("a", "b", "c", "a") + frm := FromArrayMap[string, string](Mg.Second[string]()) + + f := frm(T.Replicate2[string]) + + res1 := f(src1) + + assert.Equal(t, map[string]string{ + "a": "a", + "b": "b", + "c": "c", + }, res1) + + src2 := A.From("A", "B", "C", "A") + + res2 := f(src2) + + assert.Equal(t, map[string]string{ + "A": "A", + "B": "B", + "C": "C", + }, res2) +}