From ee4e936183557c2919eeea8154b34c7117b5d4a9 Mon Sep 17 00:00:00 2001 From: "Dr. Carsten Leue" Date: Fri, 11 Aug 2023 22:31:36 +0200 Subject: [PATCH] fix: add array examples Signed-off-by: Dr. Carsten Leue --- array/examples_basic_test.go | 59 +++++++++++++++++++++++ array/examples_sort_test.go | 92 ++++++++++++++++++++++++++++++++++++ array/generic/sort.go | 9 ++++ array/sort.go | 5 ++ option/ord.go | 38 +++++++++++++++ option/ord_test.go | 46 ++++++++++++++++++ ord/ord.go | 5 ++ 7 files changed, 254 insertions(+) create mode 100644 array/examples_basic_test.go create mode 100644 array/examples_sort_test.go create mode 100644 option/ord.go create mode 100644 option/ord_test.go diff --git a/array/examples_basic_test.go b/array/examples_basic_test.go new file mode 100644 index 0000000..c0d5835 --- /dev/null +++ b/array/examples_basic_test.go @@ -0,0 +1,59 @@ +// 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 array + +import ( + "fmt" + + F "github.com/IBM/fp-go/function" + O "github.com/IBM/fp-go/option" +) + +// Example_basic adapts examples from [https://github.com/inato/fp-ts-cheatsheet#basic-manipulation] +func Example_basic() { + + someArray := From(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) // []int + + isEven := func(num int) bool { + return num%2 == 0 + } + + square := func(num int) int { + return num * num + } + + // filter and map + result := F.Pipe2( + someArray, + Filter(isEven), + Map(square), + ) // [0 4 16 36 64] + + // or in one go with filterMap + resultFilterMap := F.Pipe1( + someArray, + FilterMap( + F.Flow2(O.FromPredicate(isEven), O.Map(square)), + ), + ) + + fmt.Println(result) + fmt.Println(resultFilterMap) + + // Output: + // [0 4 16 36 64] + // [0 4 16 36 64] +} diff --git a/array/examples_sort_test.go b/array/examples_sort_test.go new file mode 100644 index 0000000..0a147dc --- /dev/null +++ b/array/examples_sort_test.go @@ -0,0 +1,92 @@ +// 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 array + +import ( + "fmt" + + F "github.com/IBM/fp-go/function" + I "github.com/IBM/fp-go/number/integer" + O "github.com/IBM/fp-go/option" + "github.com/IBM/fp-go/ord" + S "github.com/IBM/fp-go/string" +) + +type user struct { + name string + age O.Option[int] +} + +func (user user) GetName() string { + return user.name +} + +func (user user) GetAge() O.Option[int] { + return user.age +} + +// Example_sort adapts examples from [https://github.com/inato/fp-ts-cheatsheet#sort-elements-with-ord] +func Example_sort() { + + strings := From("zyx", "abc", "klm") + + sortedStrings := F.Pipe1( + strings, + Sort(S.Ord), + ) // => ['abc', 'klm', 'zyx'] + + // reverse sort + reverseSortedStrings := F.Pipe1( + strings, + Sort(ord.Reverse(S.Ord)), + ) // => ['zyx', 'klm', 'abc'] + + // sort Option + optionalNumbers := From(O.Some(1337), O.None[int](), O.Some(42)) + + sortedNums := F.Pipe1( + optionalNumbers, + Sort(O.Ord(I.Ord)), + ) + + // complex object with different rules + byName := F.Pipe1( + S.Ord, + ord.Contramap(user.GetName), + ) // ord.Ord[user] + + byAge := F.Pipe1( + O.Ord(I.Ord), + ord.Contramap(user.GetAge), + ) // ord.Ord[user] + + sortedUsers := F.Pipe1( + From(user{name: "a", age: O.Of(30)}, user{name: "d", age: O.Of(10)}, user{name: "c"}, user{name: "b", age: O.Of(10)}), + SortBy(From(byAge, byName)), + ) + + fmt.Println(sortedStrings) + fmt.Println(reverseSortedStrings) + fmt.Println(sortedNums) + fmt.Println(sortedUsers) + + // Output: + // [abc klm zyx] + // [zyx klm abc] + // [None[int] Some[int](42) Some[int](1337)] + // [{c {false 0}} {b {true 10}} {d {true 10}} {a {true 30}}] + +} diff --git a/array/generic/sort.go b/array/generic/sort.go index 7a4026b..340a2cd 100644 --- a/array/generic/sort.go +++ b/array/generic/sort.go @@ -45,3 +45,12 @@ func SortByKey[GA ~[]T, K, T any](ord O.Ord[K], f func(T) K) func(ma GA) GA { return cpy } } + +// SortBy implements a stable sort on the array given the provided ordering +func SortBy[GA ~[]T, GO ~[]O.Ord[T], T any](ord GO) func(ma GA) GA { + return F.Pipe2( + ord, + Fold[GO](O.Monoid[T]()), + Sort[GA, T], + ) +} diff --git a/array/sort.go b/array/sort.go index 302ac38..82033d3 100644 --- a/array/sort.go +++ b/array/sort.go @@ -29,3 +29,8 @@ func Sort[T any](ord O.Ord[T]) func(ma []T) []T { func SortByKey[K, T any](ord O.Ord[K], f func(T) K) func(ma []T) []T { return G.SortByKey[[]T](ord, f) } + +// SortBy implements a stable sort on the array given the provided ordering +func SortBy[T any](ord []O.Ord[T]) func(ma []T) []T { + return G.SortBy[[]T, []O.Ord[T]](ord) +} diff --git a/option/ord.go b/option/ord.go new file mode 100644 index 0000000..9219812 --- /dev/null +++ b/option/ord.go @@ -0,0 +1,38 @@ +// 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 option + +import ( + C "github.com/IBM/fp-go/constraints" + F "github.com/IBM/fp-go/function" + "github.com/IBM/fp-go/ord" +) + +// Constructs an order for [Option] +func Ord[A any](a ord.Ord[A]) ord.Ord[Option[A]] { + // some convenient shortcuts + fld := Fold( + F.Constant(Fold(F.Constant(0), F.Constant1[A](-1))), + F.Flow2(F.Curry2(a.Compare), F.Bind1st(Fold[A, int], F.Constant(1))), + ) + // convert to an ordering predicate + return ord.MakeOrd(F.Uncurry2(fld), Eq(ord.ToEq(a)).Equals) +} + +// FromStrictCompare constructs an [Ord] from the canonical comparison function +func FromStrictCompare[A C.Ordered]() ord.Ord[Option[A]] { + return Ord(ord.FromStrictCompare[A]()) +} diff --git a/option/ord_test.go b/option/ord_test.go new file mode 100644 index 0000000..1006efe --- /dev/null +++ b/option/ord_test.go @@ -0,0 +1,46 @@ +// 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 option + +import ( + "testing" + + S "github.com/IBM/fp-go/string" + "github.com/stretchr/testify/assert" +) + +// it('getOrd', () => { +// const OS = _.getOrd(S.Ord) +// U.deepStrictEqual(OS.compare(_.none, _.none), 0) +// U.deepStrictEqual(OS.compare(_.some('a'), _.none), 1) +// U.deepStrictEqual(OS.compare(_.none, _.some('a')), -1) +// U.deepStrictEqual(OS.compare(_.some('a'), _.some('a')), 0) +// U.deepStrictEqual(OS.compare(_.some('a'), _.some('b')), -1) +// U.deepStrictEqual(OS.compare(_.some('b'), _.some('a')), 1) +// }) + +func TestOrd(t *testing.T) { + + os := Ord(S.Ord) + + assert.Equal(t, 0, os.Compare(None[string](), None[string]())) + assert.Equal(t, 1, os.Compare(Some("a"), None[string]())) + assert.Equal(t, -1, os.Compare(None[string](), Some("a"))) + assert.Equal(t, 0, os.Compare(Some("a"), Some("a"))) + assert.Equal(t, -1, os.Compare(Some("a"), Some("b"))) + assert.Equal(t, 1, os.Compare(Some("b"), Some("a"))) + +} diff --git a/ord/ord.go b/ord/ord.go index af68488..d90e387 100644 --- a/ord/ord.go +++ b/ord/ord.go @@ -40,6 +40,11 @@ func (self ord[T]) Compare(x, y T) int { return self.c(x, y) } +// ToEq converts an [Ord] to [E.Eq] +func ToEq[T any](o Ord[T]) E.Eq[T] { + return o +} + // MakeOrd creates an instance of an Ord func MakeOrd[T any](c func(x, y T) int, e func(x, y T) bool) Ord[T] { return ord[T]{c: c, e: e}