diff --git a/either/array.go b/either/array.go index f6104e6..cf713df 100644 --- a/either/array.go +++ b/either/array.go @@ -44,3 +44,15 @@ func SequenceArrayG[GA ~[]A, GOA ~[]Either[E, A], E, A any](ma GOA) Either[E, GA func SequenceArray[E, A any](ma []Either[E, A]) Either[E, []A] { return SequenceArrayG[[]A](ma) } + +// CompactArrayG discards the none values and keeps the some values +func CompactArrayG[A1 ~[]Either[E, A], A2 ~[]A, E, A any](fa A1) A2 { + return RA.Reduce(fa, func(out A2, value Either[E, A]) A2 { + return MonadFold(value, F.Constant1[E](out), F.Bind1st(RA.Append[A2, A], out)) + }, make(A2, len(fa))) +} + +// CompactArray discards the none values and keeps the some values +func CompactArray[E, A any](fa []Either[E, A]) []A { + return CompactArrayG[[]Either[E, A], []A](fa) +} diff --git a/either/record.go b/either/record.go index 0b3d572..bd326f1 100644 --- a/either/record.go +++ b/either/record.go @@ -43,3 +43,24 @@ func SequenceRecordG[GA ~map[K]A, GOA ~map[K]Either[E, A], K comparable, E, A an func SequenceRecord[K comparable, E, A any](ma map[K]Either[E, A]) Either[E, map[K]A] { return SequenceRecordG[map[K]A](ma) } + +func upsertAtReadWrite[M ~map[K]V, K comparable, V any](r M, k K, v V) M { + r[k] = v + return r +} + +// CompactRecordG discards the noe values and keeps the some values +func CompactRecordG[M1 ~map[K]Either[E, A], M2 ~map[K]A, K comparable, E, A any](m M1) M2 { + out := make(M2) + onLeft := F.Constant1[E](out) + return RR.ReduceWithIndex(m, func(key K, _ M2, value Either[E, A]) M2 { + return MonadFold(value, onLeft, func(v A) M2 { + return upsertAtReadWrite(out, key, v) + }) + }, out) +} + +// CompactRecord discards all none values and keeps the somes +func CompactRecord[K comparable, E, A any](m map[K]Either[E, A]) map[K]A { + return CompactRecordG[map[K]Either[E, A], map[K]A](m) +} diff --git a/either/record_test.go b/either/record_test.go new file mode 100644 index 0000000..8307b5d --- /dev/null +++ b/either/record_test.go @@ -0,0 +1,37 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package either + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCompactRecord(t *testing.T) { + // make the map + m := make(map[string]Either[string, int]) + m["foo"] = Left[int]("error") + m["bar"] = Right[string](1) + // compact it + m1 := CompactRecord(m) + // check expected + exp := map[string]int{ + "bar": 1, + } + + assert.Equal(t, exp, m1) +} diff --git a/option/array.go b/option/array.go index 3bc7027..19c273e 100644 --- a/option/array.go +++ b/option/array.go @@ -44,3 +44,15 @@ func SequenceArrayG[GA ~[]A, GOA ~[]Option[A], A any](ma GOA) Option[GA] { func SequenceArray[A any](ma []Option[A]) Option[[]A] { return SequenceArrayG[[]A](ma) } + +// CompactArrayG discards the none values and keeps the some values +func CompactArrayG[A1 ~[]Option[A], A2 ~[]A, A any](fa A1) A2 { + return RA.Reduce(fa, func(out A2, value Option[A]) A2 { + return MonadFold(value, F.Constant(out), F.Bind1st(RA.Append[A2, A], out)) + }, make(A2, len(fa))) +} + +// CompactArray discards the none values and keeps the some values +func CompactArray[A any](fa []Option[A]) []A { + return CompactArrayG[[]Option[A], []A](fa) +} diff --git a/option/record.go b/option/record.go index cc51037..af9181b 100644 --- a/option/record.go +++ b/option/record.go @@ -44,3 +44,21 @@ func SequenceRecordG[GA ~map[K]A, GOA ~map[K]Option[A], K comparable, A any](ma func SequenceRecord[K comparable, A any](ma map[K]Option[A]) Option[map[K]A] { return SequenceRecordG[map[K]A](ma) } + +func upsertAtReadWrite[M ~map[K]V, K comparable, V any](r M, k K, v V) M { + r[k] = v + return r +} + +// CompactRecordG discards the noe values and keeps the some values +func CompactRecordG[M1 ~map[K]Option[A], M2 ~map[K]A, K comparable, A any](m M1) M2 { + bnd := F.Bind12of3(upsertAtReadWrite[M2]) + return RR.ReduceWithIndex(m, func(key K, m M2, value Option[A]) M2 { + return MonadFold(value, F.Constant(m), bnd(m, key)) + }, make(M2)) +} + +// CompactRecord discards the noe values and keeps the some values +func CompactRecord[K comparable, A any](m map[K]Option[A]) map[K]A { + return CompactRecordG[map[K]Option[A], map[K]A](m) +} diff --git a/option/record_test.go b/option/record_test.go index e0b4ddf..b6f5640 100644 --- a/option/record_test.go +++ b/option/record_test.go @@ -30,3 +30,18 @@ func TestSequenceRecord(t *testing.T) { "b": Of("B"), })) } + +func TestCompactRecord(t *testing.T) { + // make the map + m := make(map[string]Option[int]) + m["foo"] = None[int]() + m["bar"] = Some(1) + // compact it + m1 := CompactRecord(m) + // check expected + exp := map[string]int{ + "bar": 1, + } + + assert.Equal(t, exp, m1) +} diff --git a/option/testing/laws.go b/option/testing/laws.go index e4c5090..d78694c 100644 --- a/option/testing/laws.go +++ b/option/testing/laws.go @@ -23,7 +23,7 @@ import ( O "github.com/IBM/fp-go/option" ) -// AssertLaws asserts the apply monad laws for the `Either` monad +// AssertLaws asserts the apply monad laws for the [Option] monad func AssertLaws[A, B, C any](t *testing.T, eqa EQ.Eq[A], eqb EQ.Eq[B],