diff --git a/iterator/stateless/first.go b/iterator/stateless/first.go new file mode 100644 index 0000000..7f510d9 --- /dev/null +++ b/iterator/stateless/first.go @@ -0,0 +1,26 @@ +// 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 stateless + +import ( + G "github.com/IBM/fp-go/iterator/stateless/generic" + O "github.com/IBM/fp-go/option" +) + +// First returns the first item in an iterator if such an item exists +func First[U any](mu Iterator[U]) O.Option[U] { + return G.First[Iterator[U]](mu) +} diff --git a/iterator/stateless/first_test.go b/iterator/stateless/first_test.go new file mode 100644 index 0000000..0ee3c8a --- /dev/null +++ b/iterator/stateless/first_test.go @@ -0,0 +1,41 @@ +// 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 stateless + +import ( + "testing" + + O "github.com/IBM/fp-go/option" + "github.com/stretchr/testify/assert" +) + +func TestFirst(t *testing.T) { + + seq := From(1, 2, 3) + + fst := First(seq) + + assert.Equal(t, O.Of(1), fst) +} + +func TestNoFirst(t *testing.T) { + + seq := Empty[int]() + + fst := First(seq) + + assert.Equal(t, O.None[int](), fst) +} diff --git a/iterator/stateless/generic/first.go b/iterator/stateless/generic/first.go new file mode 100644 index 0000000..8eff4fe --- /dev/null +++ b/iterator/stateless/generic/first.go @@ -0,0 +1,30 @@ +// 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 generic + +import ( + F "github.com/IBM/fp-go/function" + O "github.com/IBM/fp-go/option" + T "github.com/IBM/fp-go/tuple" +) + +// First returns the first item in an iterator if such an item exists +func First[GU ~func() O.Option[T.Tuple2[GU, U]], U any](mu GU) O.Option[U] { + return F.Pipe1( + mu(), + O.Map(T.Second[GU, U]), + ) +} diff --git a/iterator/stateless/generic/iterator.go b/iterator/stateless/generic/iterator.go index e614ba8..40aa483 100644 --- a/iterator/stateless/generic/iterator.go +++ b/iterator/stateless/generic/iterator.go @@ -49,18 +49,21 @@ func FromArray[GU ~func() O.Option[T.Tuple2[GU, U]], US ~[]U, U any](as US) GU { })(as) } +// reduce applies a function for each value of the iterator with a floating result +func reduce[GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](as GU, f func(V, U) V, initial V) V { + next, ok := O.Unwrap(as()) + current := initial + for ok { + // next (with bad side effect) + current = f(current, next.F2) + next, ok = O.Unwrap(next.F1()) + } + return current +} + // Reduce applies a function for each value of the iterator with a floating result func Reduce[GU ~func() O.Option[T.Tuple2[GU, U]], U, V any](f func(V, U) V, initial V) func(GU) V { - return func(as GU) V { - next, ok := O.Unwrap(as()) - current := initial - for ok { - // next (with bad side effect) - current = f(current, next.F2) - next, ok = O.Unwrap(next.F1()) - } - return current - } + return F.Bind23of3(reduce[GU, U, V])(f, initial) } // ToArray converts the iterator to an array diff --git a/iterator/stateless/generic/last.go b/iterator/stateless/generic/last.go new file mode 100644 index 0000000..6f6f61f --- /dev/null +++ b/iterator/stateless/generic/last.go @@ -0,0 +1,27 @@ +// 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 generic + +import ( + F "github.com/IBM/fp-go/function" + O "github.com/IBM/fp-go/option" + T "github.com/IBM/fp-go/tuple" +) + +// Last returns the last item in an iterator if such an item exists +func Last[GU ~func() O.Option[T.Tuple2[GU, U]], U any](mu GU) O.Option[U] { + return reduce(mu, F.Ignore1of2[O.Option[U]](O.Of[U]), O.None[U]()) +} diff --git a/iterator/stateless/last.go b/iterator/stateless/last.go new file mode 100644 index 0000000..d5b560f --- /dev/null +++ b/iterator/stateless/last.go @@ -0,0 +1,27 @@ +// 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 stateless + +import ( + G "github.com/IBM/fp-go/iterator/stateless/generic" + O "github.com/IBM/fp-go/option" +) + +// Last returns the last item in an iterator if such an item exists +// Note that the function will consume the [Iterator] in this call completely, to identify the last element. Do not use this for infinite iterators +func Last[U any](mu Iterator[U]) O.Option[U] { + return G.Last[Iterator[U]](mu) +} diff --git a/iterator/stateless/last_test.go b/iterator/stateless/last_test.go new file mode 100644 index 0000000..9b0db2d --- /dev/null +++ b/iterator/stateless/last_test.go @@ -0,0 +1,41 @@ +// 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 stateless + +import ( + "testing" + + O "github.com/IBM/fp-go/option" + "github.com/stretchr/testify/assert" +) + +func TestLast(t *testing.T) { + + seq := From(1, 2, 3) + + fst := Last(seq) + + assert.Equal(t, O.Of(3), fst) +} + +func TestNoLast(t *testing.T) { + + seq := Empty[int]() + + fst := Last(seq) + + assert.Equal(t, O.None[int](), fst) +}