mirror of
https://github.com/duke-git/lancet.git
synced 2026-03-01 00:35:28 +08:00
Compare commits
5 Commits
5722c724e6
...
eb66d038ac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb66d038ac | ||
|
|
a99ada5ee1 | ||
|
|
a87faf5453 | ||
|
|
ab6fec2f69 | ||
|
|
7b290989f5 |
174
iterator/iterator.go
Normal file
174
iterator/iterator.go
Normal file
@@ -0,0 +1,174 @@
|
||||
// Copyright 2022 dudaodong@gmail.com. All rights resulterved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package iterator provides a way to iterate over values stored in containers.
|
||||
// note:
|
||||
// 1. Full feature iterator is complicated, this pacakge is just a experiment to explore how iterators could work in Go.
|
||||
// 2. The functionality of this package is very simple and limited, may not meet the actual dev needs.
|
||||
// 3. It is currently under development, unstable, and will not be completed for some time in the future.
|
||||
// So, based on above factors, you may not use it in production. but, anyone is welcome to improve it.
|
||||
// Hope that Go can support iterator in future. see https://github.com/golang/go/discussions/54245 and https://github.com/golang/go/discussions/56413
|
||||
package iterator
|
||||
|
||||
import "github.com/duke-git/lancet/v2/lancetconstraints"
|
||||
|
||||
// Iterator supports iterating over a sequence of values of type `E`.
|
||||
type Iterator[T any] interface {
|
||||
// Next checks if there is a next value in the iteration or not
|
||||
HasNext() bool
|
||||
// Next returns the next value in the iteration if there is one,
|
||||
// and reports whether the returned value is valid.
|
||||
// Once Next returns ok==false, the iteration is over,
|
||||
// and all subsequent calls will return ok==false.
|
||||
Next() (item T, ok bool)
|
||||
}
|
||||
|
||||
// StopIterator is an interface for stopping Iterator.
|
||||
type StopIterator[T any] interface {
|
||||
Iterator[T]
|
||||
|
||||
// Stop indicates that the iterator will no longer be used.
|
||||
// After a call to Stop, future calls to Next may panic.
|
||||
// Stop may be called multiple times;
|
||||
// all calls after the first will have no effect.
|
||||
Stop()
|
||||
}
|
||||
|
||||
// DeleteIter is an Iter that implements a Delete method.
|
||||
type DeleteIterator[T any] interface {
|
||||
Iterator[T]
|
||||
|
||||
// Delete deletes the current iterator element;
|
||||
// that is, the one returned by the last call to Next.
|
||||
// Delete should panic if called before Next or after
|
||||
// Next returns false.
|
||||
Delete()
|
||||
}
|
||||
|
||||
// SetIterator is an Iter that implements a Set method.
|
||||
type SetIterator[T any] interface {
|
||||
Iterator[T]
|
||||
|
||||
// Set replaces the current iterator element with v.
|
||||
// Set should panic if called before Next or after
|
||||
// Next returns false.
|
||||
Set(v T)
|
||||
}
|
||||
|
||||
// PrevIterator is an iterator with a Prev method.
|
||||
type PrevIterator[T any] interface {
|
||||
Iterator[T]
|
||||
|
||||
// Prev moves the iterator to the previous position.
|
||||
// After calling Prev, Next will return the value at
|
||||
// that position in the container. For example, after
|
||||
// it.Next() returning (v, true)
|
||||
// it.Prev()
|
||||
// another call to it.Next will again return (v, true).
|
||||
// Calling Prev before calling Next may panic.
|
||||
// Calling Prev after Next returns false will move
|
||||
// to the last element, or, if there are no elements,
|
||||
// to the iterator's initial state.
|
||||
Prev()
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Functions that create an Iterator from some other type. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FromSlice returns an iterator over a slice of data.
|
||||
func FromSlice[T any](slice []T) Iterator[T] {
|
||||
return &sliceIterator[T]{slice: slice, index: -1}
|
||||
}
|
||||
|
||||
func ToSlice[T any](iter Iterator[T]) []T {
|
||||
result := []T{}
|
||||
for item, ok := iter.Next(); ok; item, ok = iter.Next() {
|
||||
result = append(result, item)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type sliceIterator[T any] struct {
|
||||
slice []T
|
||||
index int
|
||||
}
|
||||
|
||||
func (iter *sliceIterator[T]) HasNext() bool {
|
||||
return iter.index < len(iter.slice)-1
|
||||
}
|
||||
|
||||
func (iter *sliceIterator[T]) Next() (T, bool) {
|
||||
iter.index++
|
||||
ok := iter.index >= 0 && iter.index < len(iter.slice)
|
||||
var item T
|
||||
if ok {
|
||||
item = iter.slice[iter.index]
|
||||
}
|
||||
return item, ok
|
||||
|
||||
// if len(iter.slice) == 0 {
|
||||
// var zero T
|
||||
// return zero, false
|
||||
// }
|
||||
// iter.index++
|
||||
// item := iter.slice[0]
|
||||
// iter.slice = iter.slice[1:]
|
||||
// return item, true
|
||||
}
|
||||
|
||||
// Prev implements PrevIterator.
|
||||
func (iter *sliceIterator[T]) Prev() {
|
||||
if iter.index == -1 {
|
||||
panic("Next function should be called Prev")
|
||||
}
|
||||
if iter.HasNext() {
|
||||
iter.index--
|
||||
} else {
|
||||
iter.index = len(iter.slice) - 1
|
||||
}
|
||||
}
|
||||
|
||||
// Set implements SetIterator.
|
||||
func (iter *sliceIterator[T]) Set(value T) {
|
||||
if iter.index == -1 {
|
||||
panic("Next function should be called Set")
|
||||
}
|
||||
if iter.index >= len(iter.slice) || len(iter.slice) == 0 {
|
||||
panic("No element in current iterator")
|
||||
}
|
||||
iter.slice[iter.index] = value
|
||||
}
|
||||
|
||||
// FromRange creates a iterator which returns the numeric range between start inclusive and end
|
||||
// exclusive by the step size. start should be less than end, step shoud be positive.
|
||||
func FromRange[T lancetconstraints.Number](start, end, step T) Iterator[T] {
|
||||
if end < start {
|
||||
panic("RangeIterator: start should be before end")
|
||||
} else if step <= 0 {
|
||||
panic("RangeIterator: step should be positive")
|
||||
}
|
||||
|
||||
return &rangeIterator[T]{start: start, end: end, step: step}
|
||||
}
|
||||
|
||||
type rangeIterator[T lancetconstraints.Number] struct {
|
||||
start, end, step T
|
||||
}
|
||||
|
||||
func (iter *rangeIterator[T]) HasNext() bool {
|
||||
if iter.start >= iter.end {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (iter *rangeIterator[T]) Next() (T, bool) {
|
||||
if iter.start >= iter.end {
|
||||
var zero T
|
||||
return zero, false
|
||||
}
|
||||
num := iter.start
|
||||
iter.start += iter.step
|
||||
return num, true
|
||||
}
|
||||
50
iterator/iterator_test.go
Normal file
50
iterator/iterator_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2022 dudaodong@gmail.com. All rights resulterved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package iterator implements some feature of C++ STL iterators
|
||||
package iterator
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestSliceIterator(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSliceIterator")
|
||||
|
||||
// HashNext
|
||||
t.Run("slice iterator HasNext: ", func(t *testing.T) {
|
||||
iter1 := FromSlice([]int{1, 2, 3, 4})
|
||||
for {
|
||||
item, _ := iter1.Next()
|
||||
|
||||
if item == 4 {
|
||||
assert.Equal(false, iter1.HasNext())
|
||||
break
|
||||
} else {
|
||||
assert.Equal(true, iter1.HasNext())
|
||||
}
|
||||
}
|
||||
|
||||
iter2 := FromSlice([]int{})
|
||||
assert.Equal(false, iter2.HasNext())
|
||||
})
|
||||
|
||||
//Next
|
||||
t.Run("slice iterator Next: ", func(t *testing.T) {
|
||||
iter1 := FromSlice([]int{1, 2, 3, 4})
|
||||
for i := 0; i < 4; i++ {
|
||||
item, ok := iter1.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
assert.Equal(i+1, item)
|
||||
}
|
||||
|
||||
iter2 := FromSlice([]int{})
|
||||
_, ok := iter2.Next()
|
||||
assert.Equal(false, ok)
|
||||
})
|
||||
|
||||
}
|
||||
61
iterator/operation.go
Normal file
61
iterator/operation.go
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2022 dudaodong@gmail.com. All rights resulterved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package iterator provides a way to iterate over values stored in containers.
|
||||
// note:
|
||||
// 1. Full feature iterator is complicated, this pacakge is just a experiment to explore how iterators could work in Go.
|
||||
// 2. The functionality of this package is very simple and limited, may not meet the actual dev needs.
|
||||
// 3. It is currently under development, unstable, and will not be completed for some time in the future.
|
||||
// So, based on above factors, you may not use it in production. but, anyone is welcome to improve it.
|
||||
// Hope that Go can support iterator in future. see https://github.com/golang/go/discussions/54245 and https://github.com/golang/go/discussions/56413
|
||||
package iterator
|
||||
|
||||
// Map creates a new iterator which applies a function to all items of input iterator.
|
||||
func Map[T any, U any](iter Iterator[T], iteratee func(item T) U) Iterator[U] {
|
||||
return &mapIterator[T, U]{
|
||||
iter: iter,
|
||||
iteratee: iteratee,
|
||||
}
|
||||
}
|
||||
|
||||
type mapIterator[T any, U any] struct {
|
||||
iter Iterator[T]
|
||||
iteratee func(T) U
|
||||
}
|
||||
|
||||
func (mr *mapIterator[T, U]) HasNext() bool {
|
||||
return mr.iter.HasNext()
|
||||
}
|
||||
|
||||
func (mr *mapIterator[T, U]) Next() (U, bool) {
|
||||
var zero U
|
||||
item, ok := mr.iter.Next()
|
||||
if !ok {
|
||||
return zero, false
|
||||
}
|
||||
return mr.iteratee(item), true
|
||||
}
|
||||
|
||||
// Filter creates a new iterator that returns only the items that pass specified predicate function.
|
||||
func Filter[T any](iter Iterator[T], predicateFunc func(item T) bool) Iterator[T] {
|
||||
return &filterIterator[T]{iter: iter, predicateFunc: predicateFunc}
|
||||
}
|
||||
|
||||
type filterIterator[T any] struct {
|
||||
iter Iterator[T]
|
||||
predicateFunc func(T) bool
|
||||
}
|
||||
|
||||
func (fr *filterIterator[T]) Next() (T, bool) {
|
||||
for item, ok := fr.iter.Next(); ok; item, ok = fr.iter.Next() {
|
||||
if fr.predicateFunc(item) {
|
||||
return item, true
|
||||
}
|
||||
}
|
||||
var zero T
|
||||
return zero, false
|
||||
}
|
||||
|
||||
func (fr *filterIterator[T]) HasNext() bool {
|
||||
return fr.iter.HasNext()
|
||||
}
|
||||
@@ -170,33 +170,29 @@ func EqualWith[T, U any](slice1 []T, slice2 []U, comparator func(T, U) bool) boo
|
||||
|
||||
// Every return true if all of the values in the slice pass the predicate function.
|
||||
func Every[T any](slice []T, predicate func(index int, item T) bool) bool {
|
||||
var currentLength int
|
||||
|
||||
for i, v := range slice {
|
||||
if predicate(i, v) {
|
||||
currentLength++
|
||||
if !predicate(i, v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return currentLength == len(slice)
|
||||
return true
|
||||
}
|
||||
|
||||
// None return true if all the values in the slice mismatch the criteria
|
||||
func None[T any](slice []T, predicate func(index int, item T) bool) bool {
|
||||
|
||||
var currentLength int
|
||||
l := 0
|
||||
for i, v := range slice {
|
||||
if !predicate(i, v) {
|
||||
currentLength++
|
||||
l++
|
||||
}
|
||||
}
|
||||
|
||||
return currentLength == len(slice)
|
||||
return l == len(slice)
|
||||
}
|
||||
|
||||
// Some return true if any of the values in the list pass the predicate function.
|
||||
func Some[T any](slice []T, predicate func(index int, item T) bool) bool {
|
||||
|
||||
for i, v := range slice {
|
||||
if predicate(i, v) {
|
||||
return true
|
||||
@@ -421,6 +417,15 @@ func ReplaceAll[T comparable](slice []T, old T, new T) []T {
|
||||
return Replace(slice, old, new, -1)
|
||||
}
|
||||
|
||||
// Repeat creates a slice with length n whose elements are param `item`.
|
||||
func Repeat[T any](item T, n int) []T {
|
||||
result := make([]T, n)
|
||||
for i := range result {
|
||||
result[i] = item
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// InterfaceSlice convert param to slice of interface.
|
||||
func InterfaceSlice(slice any) []any {
|
||||
sv := sliceValue(slice)
|
||||
@@ -618,6 +623,17 @@ func UnionBy[T any, V comparable](predicate func(item T) V, slices ...[]T) []T {
|
||||
return result
|
||||
}
|
||||
|
||||
// Merge all given slices into one slice
|
||||
func Merge[T any](slices ...[]T) []T {
|
||||
result := make([]T, 0)
|
||||
|
||||
for _, item := range slices {
|
||||
result = append(result, item...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Intersection creates a slice of unique values that included by all slices.
|
||||
func Intersection[T comparable](slices ...[]T) []T {
|
||||
if len(slices) == 0 {
|
||||
|
||||
@@ -446,6 +446,18 @@ func TestUnionBy(t *testing.T) {
|
||||
assert.Equal(result, []int{0, 2, 4, 10})
|
||||
}
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestMerge")
|
||||
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
s2 := []int{2, 3, 4, 5}
|
||||
s3 := []int{4, 5, 6}
|
||||
|
||||
assert.Equal([]int{1, 2, 3, 4, 2, 3, 4, 5, 4, 5, 6}, Merge(s1, s2, s3))
|
||||
assert.Equal([]int{1, 2, 3, 4, 2, 3, 4, 5}, Merge(s1, s2))
|
||||
assert.Equal([]int{2, 3, 4, 5, 4, 5, 6}, Merge(s2, s3))
|
||||
}
|
||||
|
||||
func TestIntersection(t *testing.T) {
|
||||
s1 := []int{1, 2, 2, 3}
|
||||
s2 := []int{1, 2, 3, 4}
|
||||
@@ -672,3 +684,11 @@ func TestKeyBy(t *testing.T) {
|
||||
|
||||
assert.Equal(result, map[int]string{1: "a", 2: "ab", 3: "abc"})
|
||||
}
|
||||
|
||||
func TestRepeat(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestRepeat")
|
||||
|
||||
result := Repeat("a", 6)
|
||||
|
||||
assert.Equal(result, []string{"a", "a", "a", "a", "a", "a"})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user