Skip to content

Commit

Permalink
add cycle iterator
Browse files Browse the repository at this point in the history
  • Loading branch information
0x5a17ed committed May 18, 2024
1 parent 46ccaf3 commit 8d83b66
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 0 deletions.
64 changes: 64 additions & 0 deletions iters/valit/cycle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) 2024 individual contributors.
//
// 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
//
// <https://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 valit

import (
"github.com/0x5a17ed/itkit"
)

// CycleIterator represents an iterator yielding items from a given
// source iterable and saving a copy of each returned item. When the
// given source iterable is exhausted, returns items from the saved
// list. The iterable repeats the process forever.
type CycleIterator[T any] struct {
src itkit.Iterator[T]

cur T
repeating bool
copies []T
index int
}

// Value implements the [itkit.Iterator.Value] interface.
func (it *CycleIterator[T]) Value() T {
return it.cur
}

// Next implements the [itkit.Iterator.Next] interface.
func (it *CycleIterator[T]) Next() bool {
if !it.repeating {
if it.src.Next() {
it.cur = it.src.Value()
it.copies = append(it.copies, it.cur)
return true
}
it.repeating = true
}

if len(it.copies) == 0 {
return false
}

it.cur, it.index = it.copies[it.index], it.index+1
if it.index >= len(it.copies) {
it.index = 0
}
return true
}

// Cycle returns a new [CycleIterator] value.
func Cycle[T any](src itkit.Iterator[T]) itkit.Iterator[T] {
return &CycleIterator[T]{src: src}
}
55 changes: 55 additions & 0 deletions iters/valit/cycle_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) 2024 individual contributors.
//
// 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
//
// <https://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 valit_test

import (
"testing"

"github.com/0x5a17ed/itkit/iters/runeit"
"github.com/0x5a17ed/itkit/iters/valit"
"github.com/0x5a17ed/itkit/itlib"
"github.com/stretchr/testify/assert"
)

func TestCycle(t *testing.T) {
t.Run("empty", func(t *testing.T) {
it := valit.Cycle(itlib.Empty[int]())

assert.False(t, it.Next())
})

tt := []struct {
name string
inp string
}{
{"single", "A"},
{"multiple", "ABCD"},
}

for _, tc := range tt {
tc := tc
t.Run(tc.name, func(t *testing.T) {
asserter := assert.New(t)

inp := []rune(tc.inp)

it := valit.Cycle[rune](runeit.InString(tc.inp))
for i := 0; i < len(inp)*2; i++ {
v := itlib.HeadOrElse(it, rune(0))
asserter.Equal(v, inp[i%len(tc.inp)])
}
})
}
}

0 comments on commit 8d83b66

Please sign in to comment.