From 2b6f48ae9a9d86a2ee252693d524839879b9bff9 Mon Sep 17 00:00:00 2001 From: leaxoy Date: Thu, 7 Dec 2023 14:06:10 +0800 Subject: [PATCH] feat(iter): update iter functions --- collections/ordered/map.go | 93 ++++++-- collections/ordered/set.go | 28 ++- collections/stack/stack.go | 18 +- iter/iter.go | 32 +++ iter/ops.go | 429 ++++++++++++++++++++++++++++++++++--- iter/rangefunc.go | 67 ++++++ maps/maps.go | 90 ++++---- sets/set.go | 76 ++++--- sets/set_test.go | 84 ++++---- slices/example_test.go | 16 +- slices/slice.go | 207 +++++++++++------- 11 files changed, 872 insertions(+), 268 deletions(-) create mode 100644 iter/rangefunc.go diff --git a/collections/ordered/map.go b/collections/ordered/map.go index 4259f5d..16f8482 100644 --- a/collections/ordered/map.go +++ b/collections/ordered/map.go @@ -59,9 +59,7 @@ func (self *Map[K, V]) Insert(key K, value V) optional.Optional[V] { } func (self *Map[K, V]) InsertIter(it iter.Seq[MapEntry[K, V]]) { - iter.ForEach(it, func(me MapEntry[K, V]) { - self.insertEntry(me) - }) + iter.CollectFunc(it, func(me MapEntry[K, V]) bool { self.insertEntry(me); return true }) } func (self *Map[K, V]) insertEntry(entry MapEntry[K, V]) { @@ -91,7 +89,7 @@ func (self *Map[K, V]) Clone() *Map[K, V] { // Reverse returns a reversed copy of the Map. func (self *Map[K, V]) Reverse() *Map[K, V] { newTree := NewMap[K, V](invert(self.cmp)) - iter.ForEach(self.EntryIter(), newTree.insertEntry) + iter.ForEach(self.Entries(), newTree.insertEntry) return newTree } @@ -101,11 +99,26 @@ func (self *Map[K, V]) ContainsKey(key K) bool { return ok } +// ContainsAll tests is all elements in [Seq] is a valid map key. +func (self *Map[K, V]) ContainsAll(it iter.Seq[K]) bool { + return it.All(self.ContainsKey) +} + +// ContainsAny tests is any elements in [Seq] is a valid map key. +func (self *Map[K, V]) ContainsAny(it iter.Seq[K]) bool { + return it.Any(self.ContainsKey) +} + // Remove removes the MapEntry for the given key. func (self *Map[K, V]) Remove(key K) { self.inner.Delete(self.keyEntry(key)) } +// RemoveIter remove all elements in [Seq]. +func (self *Map[K, V]) RemoveIter(it iter.Seq[K]) { + it.ForEach(self.Remove) +} + // First returns the first MapEntry. func (self *Map[K, V]) First() optional.Optional[MapEntry[K, V]] { return optional.FromPair(self.inner.Min()) @@ -126,29 +139,79 @@ func (self *Map[K, V]) PopLast() optional.Optional[MapEntry[K, V]] { return optional.FromPair(self.inner.PopMax()) } -// KeyIter returns an iterator over the keys in the map. -func (self *Map[K, V]) KeyIter() iter.Seq[K] { - return iter.Map(self.inner.Scan, func(e MapEntry[K, V]) K { return e.Key() }) +// Keys returns an iterator over the keys in the map. +func (self *Map[K, V]) Keys() iter.Seq[K] { + return func(yield func(K) bool) { + self.inner.Scan(func(item MapEntry[K, V]) bool { return yield(item.Key()) }) + } } -// ValueIter returns an iterator over the keys in the map. -func (self *Map[K, V]) ValueIter() iter.Seq[V] { - return iter.Map(self.inner.Scan, func(e MapEntry[K, V]) V { return e.Value() }) +func (self *Map[K, V]) KeysMut() iter.Seq[*KeyItem[K, V]] { + return func(yield func(*KeyItem[K, V]) bool) { + self.inner.Scan(func(item MapEntry[K, V]) bool { + return yield(&KeyItem[K, V]{key: item.Key(), m: self}) + }) + } } -// EntryIter returns an iterator over the keys in the map. -func (self *Map[K, V]) EntryIter() iter.Seq[MapEntry[K, V]] { - return self.inner.Scan +// Values returns an iterator over the keys in the map. +func (self *Map[K, V]) Values() iter.Seq[V] { + return func(yield func(V) bool) { + self.inner.Scan(func(item MapEntry[K, V]) bool { return yield(item.Value()) }) + } } +func (self *Map[K, V]) ValuesMut() iter.Seq[*ValueItem[K, V]] { + return func(yield func(*ValueItem[K, V]) bool) { + self.inner.Scan(func(item MapEntry[K, V]) bool { + return yield(&ValueItem[K, V]{key: item.Key(), m: self}) + }) + } +} + +// Entries returns an iterator over the keys in the map. +func (self *Map[K, V]) Entries() iter.Seq[MapEntry[K, V]] { return self.inner.Scan } + // Len returns the number of entries in the map. func (self *Map[K, V]) Len() int { return self.inner.Len() } -func (self *Map[K, V]) Merge(o *Map[K, V]) { +func (self *Map[K, V]) MergeKeep(o *Map[K, V]) { + self.MergeFunc(o, func(_ K, prev V, _ V) V { return prev }) +} + +func (self *Map[K, V]) MergeOverwrite(o *Map[K, V]) { + self.MergeFunc(o, func(key K, prev V, current V) V { return current }) +} + +func (self *Map[K, V]) MergeFunc(o *Map[K, V], solve func(key K, prev V, current V) V) { o.inner.Scan(func(item MapEntry[K, V]) bool { - self.Insert(item.Key(), item.Value()) + prev := self.GetEntry(item.Key()) + v := item.Value() + if prev.IsSome() { + v = solve(item.Key(), prev.Value().Value(), v) + } + self.Insert(item.Key(), v) return true }) } + +type KeyItem[K any, V any] struct { + key K + m *Map[K, V] +} + +func (k *KeyItem[K, V]) Key() K { return k.key } +func (k *KeyItem[K, V]) Set(v V) { k.m.Insert(k.key, v) } +func (k *KeyItem[K, V]) Remove() { k.m.Remove(k.key) } + +type ValueItem[K any, V any] struct { + val V + key K + m *Map[K, V] +} + +func (v *ValueItem[K, V]) Value() V { return v.val } +func (v *ValueItem[K, V]) Set(val V) { v.m.Insert(v.key, val) } +func (v *ValueItem[K, V]) Remove() { v.m.Remove(v.key) } diff --git a/collections/ordered/set.go b/collections/ordered/set.go index ce08727..61090e5 100644 --- a/collections/ordered/set.go +++ b/collections/ordered/set.go @@ -38,7 +38,7 @@ func (self *Set[T]) InsertMany(elements ...T) { } func (self *Set[T]) InsertIter(it iter.Seq[T]) { - iter.ForEach(it, func(t T) { self.inner.Set(t) }) + it.ForEach(func(t T) { self.inner.Set(t) }) } // Remove removes elements from the set. @@ -119,7 +119,7 @@ func (self *Set[T]) Difference(o *Set[T]) *Set[T] { // Union returns the union of the two sets. func (self *Set[T]) Union(o *Set[T]) *Set[T] { cloned := self.Clone() - iter.ForEach(o.AscendIter(), cloned.Insert) + cloned.InsertIter(o.AscendIter()) return cloned } @@ -134,16 +134,26 @@ func (self *Set[T]) SupersetOf(o *Set[T]) bool { } // Len returns the number of elements in the set. -func (self *Set[T]) Len() int { - return self.inner.Len() -} +func (self *Set[T]) Len() int { return self.inner.Len() } // AscendIter returns an iter over the set in ascend order. -func (self *Set[T]) AscendIter() iter.Seq[T] { - return self.inner.Scan +func (self *Set[T]) AscendIter() iter.Seq[T] { return self.inner.Scan } + +func (self *Set[T]) AscendIterMut() iter.Seq[*SetItem[T]] { + return iter.Map(self.inner.Scan, func(e T) *SetItem[T] { return &SetItem[T]{item: e, s: self} }) } // DescendIter returns an iter over the set in descend order. -func (self *Set[T]) DescendIter() iter.Seq[T] { - return self.inner.Reverse +func (self *Set[T]) DescendIter() iter.Seq[T] { return self.inner.Reverse } + +func (self *Set[T]) DescendIterMut() iter.Seq[*SetItem[T]] { + return iter.Map(self.inner.ReverseMut, func(e T) *SetItem[T] { return &SetItem[T]{item: e, s: self} }) } + +type SetItem[E any] struct { + item E + s *Set[E] +} + +func (s *SetItem[E]) Value() E { return s.item } +func (s *SetItem[E]) Remove() { s.s.Remove(s.item) } diff --git a/collections/stack/stack.go b/collections/stack/stack.go index ba387c9..5a59614 100644 --- a/collections/stack/stack.go +++ b/collections/stack/stack.go @@ -17,9 +17,7 @@ func FromSlice[E any](elems ...E) *Stack[E] { return &Stack[E]{elements: elems} } -func (s *Stack[E]) Push(elem E) { - s.elements = append(s.elements, elem) -} +func (s *Stack[E]) Push(elem E) { s.elements = append(s.elements, elem) } func (s *Stack[E]) Pop() (e E, ok bool) { if len(s.elements) == 0 { @@ -40,14 +38,6 @@ func (s *Stack[E]) Peek() (e E, ok bool) { return } -func (s *Stack[E]) IsEmpty() bool { - return len(s.elements) == 0 -} - -func (s *Stack[E]) Size() int { - return len(s.elements) -} - -func (s *Stack[E]) Iter() iter.Seq[E] { - return slices.BackwardSeq(s.elements) -} +func (s *Stack[E]) IsEmpty() bool { return len(s.elements) == 0 } +func (s *Stack[E]) Size() int { return len(s.elements) } +func (s *Stack[E]) Iter() iter.Seq[E] { return slices.Backward(s.elements) } diff --git a/iter/iter.go b/iter/iter.go index 89dfff8..6750a05 100644 --- a/iter/iter.go +++ b/iter/iter.go @@ -6,3 +6,35 @@ package iter // // see: https://github.com/golang/go/issues/61897 type Seq[E any] func(yield func(E) bool) + +func (s Seq[E]) ForEach(f func(E)) { ForEach(s, f) } +func (s Seq[E]) TryForEach(f func(E) error) error { return TryForEach(s, f) } +func (s Seq[E]) Map(f func(E) E) Seq[E] { return Map(s, f) } +func (s Seq[E]) MapWhile(f func(E) (E, bool)) Seq[E] { return MapWhile(s, f) } +func (s Seq[E]) TryFold(init E, f func(E, E) (E, error)) (E, error) { return TryFold(s, init, f) } +func (s Seq[E]) Fold(init E, f func(E, E) E) E { return Fold(s, init, f) } +func (s Seq[E]) Reduce(f func(E, E) E) (E, bool) { return Reduce(s, f) } +func (s Seq[E]) Filter(f func(E) bool) Seq[E] { return Filter(s, f) } +func (s Seq[E]) FilterMap(f func(E) (E, bool)) Seq[E] { return FilterMap(s, f) } +func (s Seq[E]) MaxFunc(f func(E, E) int) (E, bool) { return MaxFunc(s, f) } +func (s Seq[E]) MinFunc(f func(E, E) int) (E, bool) { return MinFunc(s, f) } +func (s Seq[E]) Find(f func(E) bool) (E, bool) { return Find(s, f) } +func (s Seq[E]) FindMap(f func(E) (E, bool)) (E, bool) { return FindMap(s, f) } +func (s Seq[E]) Index(f func(E) bool) (int, bool) { return Index(s, f) } +func (s Seq[E]) All(f func(E) bool) bool { return All(s, f) } +func (s Seq[E]) Any(f func(E) bool) bool { return Any(s, f) } +func (s Seq[E]) CountFunc(f func(E) bool) int { return CountFunc(s, f) } +func (s Seq[E]) Size() int { return Size(s) } +func (s Seq[E]) IsSortedFunc(f func(E, E) int) bool { return IsSortedFunc(s, f) } +func (s Seq[E]) StepBy(n int) Seq[E] { return StepBy(s, n) } +func (s Seq[E]) Take(n int) Seq[E] { return Take(s, n) } +func (s Seq[E]) TakeWhile(f func(E) bool) Seq[E] { return TakeWhile(s, f) } +func (s Seq[E]) Skip(n int) Seq[E] { return Skip(s, n) } +func (s Seq[E]) SkipWhile(f func(E) bool) Seq[E] { return SkipWhile(s, f) } +func (s Seq[E]) Chain(y Seq[E]) Seq[E] { return Chain(s, y) } +func (s Seq[E]) CollectFunc(collect func(E) bool) { CollectFunc(s, collect) } +func (s Seq[E]) Intersperse(sep E) Seq[E] { return Intersperse(s, sep) } +func (s Seq[E]) First(f func(E) bool) (E, bool) { return First(s, f) } +func (s Seq[E]) Last(f func(E) bool) (E, bool) { return Last(s, f) } +func (s Seq[E]) Inspect(f func(E)) Seq[E] { return Inspect(s, f) } +func (s Seq[E]) DedupFunc(f func(E, E) bool) Seq[E] { return DedupFunc(s, f) } diff --git a/iter/ops.go b/iter/ops.go index 1a4b67f..4ddfa95 100644 --- a/iter/ops.go +++ b/iter/ops.go @@ -10,35 +10,38 @@ type Tuple[A, B any] struct { Right B } +// Enumerate create a new seq which yield (index, item) pair. +// +// Example: +// +// iter.Enumerate(seq(1,2,3)) // seq: (0, 1), (1, 2), (2, 3) func Enumerate[E any](s Seq[E]) Seq[Tuple[int, E]] { i := -1 return func(yield func(Tuple[int, E]) bool) { - s(func(e E) bool { - i++ - return yield(Tuple[int, E]{Left: i, Right: e}) - }) + s(func(e E) bool { i++; return yield(Tuple[int, E]{Left: i, Right: e}) }) } } // TryForEach call f on each element in [Seq], // stopping at the first error and returning that error. func TryForEach[E any](s Seq[E], f func(E) error) (err error) { - s(func(x E) bool { - err = f(x) - return err == nil - }) + s(func(x E) bool { err = f(x); return err == nil }) return } // ForEach call f on each element in Seq. func ForEach[E any](s Seq[E], f func(E)) { - s(func(x E) bool { - f(x) - return true - }) + s(func(x E) bool { f(x); return true }) } -// Filter remove elements do not match predicate. +// Filter creates an iterator which uses a closure to +// determine if an element should be yielded. +// +// Filter will not stop until iterate finished. +// +// Example: +// +// iter.Filter(seq(1,2,3), func(x int) bool {return i%2==1}) // seq: 1, 3 func Filter[E any](s Seq[E], f func(E) bool) Seq[E] { return func(yield func(E) bool) { s(func(x E) bool { @@ -50,30 +53,127 @@ func Filter[E any](s Seq[E], f func(E) bool) Seq[E] { } } +// FilterMap creates an iterator that both filters and maps. +func FilterMap[E, T any](s Seq[E], f func(E) (T, bool)) Seq[T] { + return func(yield func(T) bool) { + s(func(e E) bool { + t, ok := f(e) + if ok { + return yield(t) + } + return true + }) + } +} + +func FilterZero[E comparable](s Seq[E]) Seq[E] { + var zero E + return Filter(s, func(e E) bool { return e != zero }) +} + +// First try to find first element in [Seq], +// if no elements in [Seq], return zero var and false. +// +// Example: +// +// iter.First(seq(1,2,3)) // 1, true +// iter.First(seq[int]()) // 0, false +func First[E any](s Seq[E], f func(E) bool) (m E, ok bool) { + return Find(s, func(e E) bool { return f(e) }) +} + +// Last try to find last element in [Seq], +// if no elements in [Seq], return zero var and false. +// +// Example: +// +// iter.Last(seq(1,2,3)) // 3, true +// iter.Last(seq[int]()) // 0, false +func Last[E any](s Seq[E], f func(E) bool) (m E, ok bool) { + return Find(s, func(e E) bool { + if f(e) { + m = e + ok = true + } + return true + }) +} + +// Map call f on each element in [Seq], and map each element to another type. +// +// Example: +// +// iter.Map(seq(1,2,3), strconv.Itoa) // seq: "1", "2", "3" func Map[E, T any](s Seq[E], f func(E) T) Seq[T] { return func(yield func(T) bool) { s(func(x E) bool { return yield(f(x)) }) } } +// MapWhile call f on each element on [Seq], and map each element to another type. +// +// Stopping after an initial false. +// +// Example: +// +// iter.MapWhile(seq("1","2","e"), func(x string) (int, bool) { +// i, err := strconv.Atoi(x) +// if err != nil { +// return 0, false +// } +// return i, true +// }) // seq: 1, 2 +func MapWhile[E, T any](s Seq[E], f func(E) (T, bool)) Seq[T] { + return func(yield func(T) bool) { + s(func(e E) bool { + t, ok := f(e) + return ok && yield(t) + }) + } +} + +// TryFold applies a function as long as it returns +// successfully, producing a single, final value. +// +// It takes two arguments: an initial value, and a closure with +// two arguments: an 'accumulator', and an element. The closure either +// returns successfully, with the value that the accumulator should have +// for the next iteration, or it returns failure, with an error value that +// is propagated back to the caller immediately (short-circuiting). +// +// Example: +// +// iter.TryFold(seq(1,2,3), 1, func(acc int, item int) (int, error) { return acc * item, nil }) // 6, nil +// iter.TryFold(seq("1", "3", "e"), 1, func(acc int, item string) (int, error) { +// x, err := strconv.Atoi(item) +// if err != nil {return 0, err} +// return acc * x, nil +// }) // 0, err func TryFold[E, A any](s Seq[E], init A, f func(A, E) (A, error)) (res A, err error) { res = init - s(func(x E) bool { - res, err = f(res, x) - return err == nil - }) + s(func(x E) bool { res, err = f(res, x); return err == nil }) return } +// Fold folds every element into an accumulator by applying an operation, +// returning the final result. +// +// Example: +// +// iter.Fold(seq(1,2,3), 1, func(acc int, e int) int { return acc * e }) // 6 func Fold[E, A any](s Seq[E], init A, f func(A, E) A) A { accum := init - s(func(x E) bool { - accum = f(accum, x) - return true - }) + s(func(x E) bool { accum = f(accum, x); return true }) return accum } +// Reduce reduces the elements to a single one, by repeatedly applying a reducing +// operation. +// +// Example: +// +// iter.Reduce(seq(1,2,3), func(x, y int) int { return x * y }) // 6, true +// iter.Reduce(seq[int](), func(x, y int) int { return x * y }) // 0, false func Reduce[E any](s Seq[E], f func(E, E) E) (result E, hasElem bool) { s(func(x E) bool { if !hasElem { @@ -87,6 +187,7 @@ func Reduce[E any](s Seq[E], f func(E, E) E) (result E, hasElem bool) { return } +// Find searches for an element of an iterator that satisfies a predicate. func Find[E any](s Seq[E], f func(E) bool) (result E, ok bool) { s(func(x E) bool { if f(x) { @@ -99,24 +200,44 @@ func Find[E any](s Seq[E], f func(E) bool) (result E, ok bool) { return } +// FindMap applies function to the elements of iterator and returns +// the first non-none result. +func FindMap[E, T any](s Seq[E], f func(E) (T, bool)) (t T, ok bool) { + s(func(e E) bool { + if x, o := f(e); o { + t = x + ok = o + return false + } + return true + }) + return +} + func Index[E any](s Seq[E], f func(E) bool) (int, bool) { var i int = -1 - s(func(x E) bool { - i++ - return !f(x) - }) + s(func(x E) bool { i++; return !f(x) }) return i, i >= 0 } +// All tests if every element of the iterator matches a predicate. +// +// Example: +// +// iter.All(seq(1,2,3), func(x int) bool { return i > 0 }) // true +// iter.Any(seq(1,2,3), func(x int) bool { return i > 2 }) // false func All[E any](s Seq[E], f func(E) bool) bool { ok := true - s(func(x E) bool { - ok = f(x) - return ok - }) + s(func(x E) bool { ok = f(x); return ok }) return ok } +// Any tests if any element of the iterator matches a predicate. +// +// Example: +// +// iter.Any(seq(1,2,3), func(x int) bool { return i % 2 == 0 }) // true +// iter.Any(seq(1,2,3), func(x int) bool { return i < 0 }) // false func Any[E any](s Seq[E], f func(E) bool) bool { var ok bool s(func(x E) bool { @@ -129,10 +250,23 @@ func Any[E any](s Seq[E], f func(E) bool) bool { return ok } +// Max returns the maximum element of an iterator. +// +// Example: +// +// iter.Max(seq(1,2,3)) // 3, true +// iter.Max(seq[int]()) // 0, false func Max[E cmp.Ordered](s Seq[E]) (m E, hasElem bool) { return MaxFunc(s, cmp.Compare[E]) } +// MaxFunc returns the element that gives the maximum value with respect to the +// specified comparison function. +// +// Example: +// +// iter.MaxFunc(seq(1,2,3), cmp.Compare[int]) // 3, true +// iter.MaxFunc(seq[int](), cmp.Compare[int]) // 0, false func MaxFunc[E any](s Seq[E], f func(E, E) int) (m E, hasElem bool) { return Reduce(s, func(l, r E) E { if f(l, r) > 0 { @@ -142,10 +276,23 @@ func MaxFunc[E any](s Seq[E], f func(E, E) int) (m E, hasElem bool) { }) } +// Min returns the maximum element of an iterator. +// +// Example: +// +// iter.Min(seq(1,2,3)) // 1, true +// iter.Min(seq[int]()) // 0, false func Min[E cmp.Ordered](s Seq[E]) (m E, hasElem bool) { return MinFunc(s, cmp.Compare[E]) } +// MinFunc returns the element that gives the maximum value with respect to the +// specified comparison function. +// +// Example: +// +// iter.MinFunc(seq(1,2,3), cmp.Compare[int]) // 1, true +// iter.MinFunc(seq[int](), cmp.Compare[int]) // 0, false func MinFunc[E any](s Seq[E], f func(E, E) int) (m E, hasElem bool) { return Reduce(s, func(l, r E) E { if f(l, r) < 0 { @@ -155,10 +302,23 @@ func MinFunc[E any](s Seq[E], f func(E, E) int) (m E, hasElem bool) { }) } +// Count consumes the iterator, counting the number of elements +// equal to the given one and returning it. +// +// Example: +// +// iter.Count(seq(1,2,3,4,5,1,2,3), 2) // 2 +// iter.Count(seq(1,2,3,4,5,1,2,3), 5) // 1 func Count[E comparable](s Seq[E], value E) int { return CountFunc(s, func(e E) bool { return value == e }) } +// CountFunc consumes the iterator, counting the number of elements +// match the predicate function and returning it. +// +// Example: +// +// iter.CountFunc(seq(1,2,3), func(x int) bool { return x % 2 == 0 }) // 1 func CountFunc[E any](s Seq[E], f func(E) bool) int { n := 0 s(func(x E) bool { @@ -170,14 +330,31 @@ func CountFunc[E any](s Seq[E], f func(E) bool) int { return n } +// Size consumes the iterator, counting the number of iterations and returning it. +// +// Example: +// +// iter.Size(seq(1,2,3)) // 3 func Size[E any](s Seq[E]) int { return CountFunc(s, func(E) bool { return true }) } +// IsSorted checks if the elements of this iterator are sorted. +// +// Example: +// +// iter.IsSorted(seq(1, 2, 3)) // true +// iter.IsSorted(seq(2, 1, 3)) // false func IsSorted[E cmp.Ordered](s Seq[E]) bool { return IsSortedFunc(s, cmp.Compare[E]) } +// IsSortedFunc checks if the elements of this iterator are sorted using the given comparator function. +// +// Example: +// +// iter.IsSortedFunc(seq(1, 2, 3), cmp.Compare[int]) // true +// iter.IsSortedFunc(seq(2, 1, 3), cmp.Compare[int]) // false func IsSortedFunc[E any](s Seq[E], f func(E, E) int) bool { var prev *E ok := true @@ -195,6 +372,32 @@ func IsSortedFunc[E any](s Seq[E], f func(E, E) int) bool { return ok } +// StepBy creates an iterator starting at the same point, but stepping by +// the given amount at each iteration. +// +// Example: +// +// iter.StepBy(seq(1,2,3,4,5), 2) // seq: 1,3,5 +func StepBy[E any](s Seq[E], n int) Seq[E] { + var i int + return func(yield func(E) bool) { + s(func(e E) bool { + if i%n == 0 && !yield(e) { + return false + } + i++ + return true + }) + } +} + +// Take creates an iterator that yields the first `n` elements, or fewer +// if the underlying iterator ends sooner. +// +// Example: +// +// iter.Take(seq(1,2,3), 2) // seq: 1,2 +// iter.Take(seq(1,2,3), 5) // seq: 1,2,3 func Take[E any](s Seq[E], n int) Seq[E] { return func(yield func(E) bool) { i := 0 @@ -208,6 +411,26 @@ func Take[E any](s Seq[E], n int) Seq[E] { } } +// TakeWhile creates an iterator that yields elements based on a predicate. +// +// Stopping after an initial `false`. +func TakeWhile[E any](s Seq[E], f func(E) bool) Seq[E] { + return func(yield func(E) bool) { + s(func(x E) bool { + if f(x) { + return yield(x) + } + return false + }) + } +} + +// Skip creates an iterator that skips the first `n` elements. +// +// Example: +// +// iter.Skip(seq(1,2,3), 1) // seq: 2,3 +// iter.Skip(seq(1,2), 3) // seq: none func Skip[E any](s Seq[E], n int) Seq[E] { return func(yield func(E) bool) { i := 0 @@ -221,6 +444,26 @@ func Skip[E any](s Seq[E], n int) Seq[E] { } } +// SkipWhile creates an iterator that [`skip`]s elements based on a predicate. +func SkipWhile[E any](s Seq[E], f func(E) bool) Seq[E] { + return func(yield func(E) bool) { + var ok bool + s(func(x E) bool { + if !ok && f(x) { + ok = true + return true + } + return yield(x) + }) + } +} + +// Distinct return an iterator adaptor that filters out elements that have +// already been produced once during the iteration. +// +// Example: +// +// iter.Distinct(seq(1,2,1,2,3)) // seq: 1,2,3 func Distinct[E comparable](s Seq[E]) Seq[E] { m := make(map[E]struct{}) return func(yield func(E) bool) { @@ -234,22 +477,144 @@ func Distinct[E comparable](s Seq[E]) Seq[E] { } } +// Dedup removes consecutive repeated elements in the [Seq]. +// +// Example: +// +// iter.Dedup(seq(1,1,2,2,3,3)) => seq: 1,2,3 +// iter.Dedup(seq(1,2,2,3,2,2)) => seq: 1,2,3,2 +func Dedup[E comparable](s Seq[E]) Seq[E] { + return DedupFunc(s, operator.Eq[E]) +} + +// DedupFunc removes all but the first of consecutive elements in +// the Seq satisfying a given equality relation. +// +// Example: +// +// iter.DedupFunc(seq(1,1,2,2,3,3), func(x int, y int) bool { return x + y < 5 }) => seq: 1,3 +// iter.DedupFunc(seq(1,2,3,2,1), func(x int, y int) bool { return x != y}) => seq: 1 +func DedupFunc[E any](s Seq[E], f func(E, E) bool) Seq[E] { + return func(yield func(E) bool) { + var prev E + var consumed bool + s(func(e E) bool { + prev = e + if !consumed { + consumed = true + return yield(e) + } + if !f(prev, e) { + return yield(e) + } + return true + }) + } +} + func Flatten[E any](s Seq[Seq[E]]) Seq[E] { return FlatMap(s, operator.Identify[Seq[E]]) } +// FlatMap call f on each element in Seq which create a new type Seq by each element. +// +// Example: +// +// iter.FlatMap(seq(1,2,3), func(x int) Seq[int] { return rangeTo(x)}) // seq: 1,1,2,1,2,3 func FlatMap[E, T any](s Seq[E], f func(E) Seq[T]) Seq[T] { return func(yield func(T) bool) { s(func(x E) bool { - shouldCountine := true + shouldContinue := true f(x)(func(e T) bool { if !yield(e) { - shouldCountine = false + shouldContinue = false return false } return true }) - return shouldCountine + return shouldContinue + }) + } +} + +// Chain takes two iterators and creates a new iterator over both in sequence. +// +// Example: +// +// iter.Chain(seq(1,2,3), seq(4,5,6)) // seq: 1,2,3,4,5,6 +func Chain[E any](x Seq[E], y Seq[E]) Seq[E] { + return func(yield func(E) bool) { + shouldBreak := false + x(func(x E) bool { + y := yield(x) + if !y { + shouldBreak = true + } + return y + }) + if !shouldBreak { + y(func(x E) bool { + return yield(x) + }) + } + } +} + +// CollectFunc call func on each element to collect it. +// +// Stopping iterate when collect returns false. +// +// collect return false means collector maybe full, +// or no need to collect more elements. +// +// Example: +// +// res := make(chan int, 2) +// iter.CollectFunc(seq(1,2,3), func (x int) bool { +// select { +// case res <- x: +// return true +// default: +// return false +// } +// }) +func CollectFunc[E any](s Seq[E], collect func(E) bool) { + s(func(x E) bool { return collect(x) }) +} + +// Intersperse creates a new iterator which places a copy of `separator` +// between adjacent items of the original iterator. +// +// Example: +// +// iter.Intersperse(seq(1,2,3), 0) // seq: 1,0,2,0,3 +func Intersperse[E any](s Seq[E], sep E) Seq[E] { + return func(yield func(E) bool) { + first := true + s(func(x E) bool { + if first { + first = false + return yield(x) + } else { + return yield(sep) && yield(x) + } + }) + } +} + +// Inspect call f on each element in [Seq]. +// +// Like ForEach, Inspect will iterate all element in Seq without stopping. +// Unlike ForEach, Inspect will not consume Seq. +// +// Example: +// +// iter.Inspect(seq(1,2,3), func(x int) { println(x) }) // seq: 1,2,3 +func Inspect[E any](s Seq[E], f func(E)) Seq[E] { + return func(yield func(E) bool) { + s(func(e E) bool { + f(e) + return yield(e) }) } } diff --git a/iter/rangefunc.go b/iter/rangefunc.go new file mode 100644 index 0000000..07c261d --- /dev/null +++ b/iter/rangefunc.go @@ -0,0 +1,67 @@ +//go:build goexperiment.rangefunc + +package iter + +import ( + "iter" + + "github.com/go-board/std/cmp" + "github.com/go-board/std/core" + "github.com/go-board/std/operator" +) + +func Cmp[E core.Ordered](x Seq[E], y Seq[E]) int { + return CmpFunc(x, y, cmp.Compare[E]) +} + +func CmpFunc[E, F any](x Seq[E], y Seq[F], f func(E, F) int) int { + itx, sx := iter.Pull(iter.Seq[E](x)) + ity, sy := iter.Pull(iter.Seq[F](y)) + defer sx() + defer sy() + for { + xe, oke := itx() + xf, okf := ity() + if oke == okf { + return f(xe, xf) + } + if oke { + return +1 + } else { + return -1 + } + } +} + +func Gt[E core.Ordered](x Seq[E], y Seq[E]) bool { return Cmp(x, y) > 0 } +func Ge[E core.Ordered](x Seq[E], y Seq[E]) bool { return Cmp(x, y) >= 0 } +func Lt[E core.Ordered](x Seq[E], y Seq[E]) bool { return Cmp(x, y) < 0 } +func Le[E core.Ordered](x Seq[E], y Seq[E]) bool { return Cmp(x, y) <= 0 } + +func Eq[E comparable](x Seq[E], y Seq[E]) bool { + return EqFunc(x, y, operator.Eq[E]) +} + +func EqFunc[E, F any](x Seq[E], y Seq[F], f func(E, F) bool) bool { + itx, sx := iter.Pull(iter.Seq[E](x)) + ity, sy := iter.Pull(iter.Seq[F](y)) + defer sx() + defer sy() + for { + xe, oke := itx() + xf, okf := ity() + return oke == okf && f(xe, xf) + } +} + +func Ne[E comparable](x Seq[E], y Seq[E]) bool { return !Eq(x, y) } + +func NeFunc[E, F any](x Seq[E], y Seq[F], f func(E, F) bool) bool { return !EqFunc(x, y, f) } + +func (s Seq[E]) CmpFunc(y Seq[E], f func(E, E) int) int { return CmpFunc(s, y, f) } +func (s Seq[E]) EqFunc(y Seq[E], f func(E, E) bool) bool { return EqFunc(s, y, f) } +func (s Seq[E]) NeFunc(y Seq[E], f func(E, E) bool) bool { return NeFunc(s, y, f) } + +func (s Seq[E]) Iter() (func() (E, bool), func()) { + return iter.Pull(iter.Seq[E](s)) +} diff --git a/maps/maps.go b/maps/maps.go index 7943d94..6f93edc 100644 --- a/maps/maps.go +++ b/maps/maps.go @@ -26,12 +26,6 @@ func Entries[K comparable, V any, M ~map[K]V](m M) iter.Seq[MapEntry[K, V]] { } } -func Collect[K comparable, V any](s iter.Seq[MapEntry[K, V]]) map[K]V { - m := make(map[K]V) - iter.ForEach(s, func(e MapEntry[K, V]) { m[e.Key()] = e.Value() }) - return m -} - // Keys return key slice of a map. func Keys[K comparable, V any, M ~map[K]V](m M) iter.Seq[K] { return func(yield func(K) bool) { @@ -54,6 +48,16 @@ func Values[K comparable, V any, M ~map[K]V](m M) iter.Seq[V] { } } +func Collect[K comparable, V any](s iter.Seq[MapEntry[K, V]]) map[K]V { + m := make(map[K]V) + CollectInto(s, m) + return m +} + +func CollectInto[K comparable, V any, M ~map[K]V](s iter.Seq[MapEntry[K, V]], m M) { + iter.CollectFunc(s, func(x MapEntry[K, V]) bool { m[x.Key()] = x.Value(); return true }) +} + // ForEach iter over the map, and call the udf on each k-v pair. func ForEach[K comparable, V any, M ~map[K]V](m M, f func(K, V)) { iter.ForEach(Entries(m), func(x MapEntry[K, V]) { @@ -61,82 +65,66 @@ func ForEach[K comparable, V any, M ~map[K]V](m M, f func(K, V)) { }) } -// Map map each k-v pair to x-y pair into a new map. +// Map call f on each k-v pair and maps to x-y pair into a new map. func Map[K, X comparable, V, Y any, M ~map[K]V](m M, f func(K, V) (X, Y)) map[X]Y { - n := make(map[X]Y) - for k, v := range m { - x, y := f(k, v) - n[x] = y - } - return n + return Collect(iter.Map(Entries(m), func(e MapEntry[K, V]) MapEntry[X, Y] { + return entry(f(e.Key(), e.Value())) + })) } -func MapKey[K, X comparable, V any, M ~map[K]V](m M, f func(K) X) map[X]V { - n := make(map[X]V) - for k, v := range m { - n[f(k)] = v - } - return n +func MapKey[K, X comparable, V any, M ~map[K]V](m M, f func(K, V) X) map[X]V { + return Map(m, func(k K, v V) (X, V) { return f(k, v), v }) } -func MapValue[K comparable, V, X any, M ~map[K]V](m M, f func(V) X) map[K]X { - n := make(map[K]X) +func MapValue[K comparable, V, X any, M ~map[K]V](m M, f func(K, V) X) map[K]X { + return Map(m, func(k K, v V) (K, X) { return k, f(k, v) }) +} + +func Retain[K comparable, V any, M ~map[K]V](m M, f func(K, V) bool) { for k, v := range m { - n[k] = f(v) + if !f(k, v) { + delete(m, k) + } } - return n } // Filter keep those elements which match the given predicate function. func Filter[K comparable, V any, M ~map[K]V](m M, f func(K, V) bool) M { - return Collect(iter.Filter(Entries(m), func(me MapEntry[K, V]) bool { - return f(me.Key(), me.Value()) - })) + return Collect(Entries(m).Filter(func(me MapEntry[K, V]) bool { return f(me.Key(), me.Value()) })) } // FilterMap keep those elements which match the given predicate function and map to new type elements. func FilterMap[K comparable, V any, M ~map[K]V, X comparable, Y any, N ~map[X]Y](m M, f func(K, V) (X, Y, bool)) N { - r := N{} - for k, v := range m { - if x, y, ok := f(k, v); ok { - r[x] = y + return Collect(iter.FilterMap(Entries(m), func(e MapEntry[K, V]) (MapEntry[X, Y], bool) { + if x, y, ok := f(e.Key(), e.Value()); ok { + return entry(x, y), true } - } - return r + return MapEntry[X, Y]{}, false + })) } -// MergeKeep merge many maps and keep first value when conflict occrured. +// MergeKeep merge many maps and keep first value when conflict occurred. func MergeKeep[K comparable, V any, M ~map[K]V](ms iter.Seq[M]) M { - x := M{} - iter.ForEach(ms, func(m M) { - ForEach(m, func(k K, v V) { - if _, ok := x[k]; !ok { - x[k] = v - } - }) - }) - return x + return MergeFunc(ms, func(key K, prev V, current V) V { return prev }) } -// MergeOverwrite merge many maps and keep last value when conflict occrured. +// MergeOverwrite merge many maps and keep last value when conflict occurred. func MergeOverwrite[K comparable, V any, M ~map[K]V](ms iter.Seq[M]) M { - x := M{} - iter.ForEach(ms, func(m M) { ForEach(m, func(k K, v V) { x[k] = v }) }) - return x + return MergeFunc(ms, func(key K, prev V, current V) V { return current }) } -// MergeFunc merge many maps and solve conflict use a udf. +// MergeFunc merge many maps and solve conflict use an udf. // // UDF has signature `func(V, V) V`, first param is previous element, // second is visit element, return element will be used. -func MergeFunc[K comparable, V any, M ~map[K]V](ms iter.Seq[M], onConflict func(V, V) V) M { +func MergeFunc[K comparable, V any, M ~map[K]V](ms iter.Seq[M], onConflict func(key K, prev V, current V) V) M { x := make(M) - iter.ForEach(ms, func(m M) { ForEach(m, func(k K, v V) { x[k] = onConflict(x[k], v) }) }) + iter.ForEach(ms, func(m M) { ForEach(m, func(k K, v V) { x[k] = onConflict(k, x[k], v) }) }) return x } -// Invert maps k-v to v-k, when conflict, the back element will overwrite the previous one. -func Invert[K comparable, V comparable, M1 ~map[K]V, M2 ~map[V]K](m M1) M2 { +// Invert maps k-v to v-k, when key conflict, the back element will overwrite the previous one. +func Invert[K, V comparable, M1 ~map[K]V, M2 ~map[V]K](m M1) M2 { m2 := make(M2) for k, v := range m { m2[v] = k diff --git a/sets/set.go b/sets/set.go index ce30d1f..616b4b3 100644 --- a/sets/set.go +++ b/sets/set.go @@ -11,15 +11,15 @@ var unit = struct{}{} type HashSet[E comparable] struct{ inner map[E]core.Unit } -var _set = NewHashSet[core.Unit]() +var _set HashSet[core.Unit] var ( _ json.Marshaler = _set _ json.Unmarshaler = _set ) -// NewHashSet returns a new empty hash set. -func NewHashSet[E comparable](elements ...E) HashSet[E] { +// FromSlice returns a new empty hash set. +func FromSlice[E comparable](elements ...E) HashSet[E] { inner := make(map[E]core.Unit, len(elements)) for _, element := range elements { inner[element] = unit @@ -35,9 +35,10 @@ func FromMapKeys[E comparable, TValue any, M ~map[E]TValue](m M) HashSet[E] { return HashSet[E]{inner: inner} } -func FromIter[E comparable](s iter.Seq[E]) HashSet[E] { +// Collect create a HashSet from [Seq]. +func Collect[E comparable](s iter.Seq[E]) HashSet[E] { set := HashSet[E]{inner: make(map[E]core.Unit)} - iter.ForEach(s, func(e E) { set.inner[e] = unit }) + set.AddIter(s) return set } @@ -60,17 +61,10 @@ func (self HashSet[E]) AddIter(s iter.Seq[E]) { // Remove removes the given key from the set. func (self HashSet[E]) Remove(key E) { - self.RemoveBy(func(k E) bool { return k == key }) + delete(self.inner, key) } -// RemoveBy remove keys from the set if the given predicate returns true. -func (self HashSet[E]) RemoveBy(predicate func(E) bool) { - for k := range self.inner { - if predicate(k) { - delete(self.inner, k) - } - } -} +func (self HashSet[E]) RemoveIter(it iter.Seq[E]) { it.ForEach(self.Remove) } // Clear removes all keys from the set. func (self HashSet[E]) Clear() { @@ -80,17 +74,19 @@ func (self HashSet[E]) Clear() { } func (self HashSet[E]) Filter(fn func(E) bool) HashSet[E] { - other := NewHashSet[E]() - iter.ForEach(self.Iter(), func(t E) { - if fn(t) { - other.Add(t) + return Collect(self.Iter().Filter(fn)) +} + +func (self HashSet[E]) Retain(fn func(E) bool) { + self.Iter().ForEach(func(e E) { + if !fn(e) { + self.Remove(e) } }) - return other } func (self HashSet[E]) Map(fn func(E) E) HashSet[E] { - m := NewHashSet[E]() + m := FromSlice[E]() for k := range self.inner { m.Add(fn(k)) } @@ -138,7 +134,7 @@ func (self HashSet[E]) Clone() HashSet[E] { // DeepCloneBy returns a copy of the set and clone each element use given clone func. func (self HashSet[E]) DeepCloneBy(clone func(E) E) HashSet[E] { - other := NewHashSet[E]() + other := FromSlice[E]() for key := range self.inner { other.Add(clone(key)) } @@ -167,12 +163,17 @@ func (self HashSet[E]) SubsetOf(other HashSet[E]) bool { // Union returns a new set containing all the elements that are in either set. func (self HashSet[E]) Union(other HashSet[E]) HashSet[E] { - union := NewHashSet[E]() - union.AddAll(self) + union := self.Clone() union.AddAll(other) return union } +func (self HashSet[E]) UnionIter(it iter.Seq[E]) HashSet[E] { + union := self.Clone() + union.AddIter(it) + return union +} + // UnionAssign union another [HashSet] into self func (self HashSet[E]) UnionAssign(other HashSet[E]) { self.AddAll(other) @@ -180,7 +181,7 @@ func (self HashSet[E]) UnionAssign(other HashSet[E]) { // Intersection returns a new set containing all the elements that are in both sets. func (self HashSet[E]) Intersection(other HashSet[E]) HashSet[E] { - intersection := NewHashSet[E]() + intersection := FromSlice[E]() for key := range self.inner { if other.Contains(key) { intersection.Add(key) @@ -191,7 +192,7 @@ func (self HashSet[E]) Intersection(other HashSet[E]) HashSet[E] { // Difference returns a new set containing all the elements that are in this set but not in the other set. func (self HashSet[E]) Difference(other HashSet[E]) HashSet[E] { - diff := NewHashSet[E]() + diff := FromSlice[E]() for key := range self.inner { if !other.Contains(key) { diff.Add(key) @@ -202,7 +203,7 @@ func (self HashSet[E]) Difference(other HashSet[E]) HashSet[E] { // SymmetricDifference returns a new set containing all the elements that are in this set or the other set but not in both. func (self HashSet[E]) SymmetricDifference(other HashSet[E]) HashSet[E] { - diff := NewHashSet[E]() + diff := FromSlice[E]() for key := range self.inner { if !other.Contains(key) { diff.Add(key) @@ -229,6 +230,10 @@ func (self HashSet[E]) Equal(other HashSet[E]) bool { return true } +func (self HashSet[E]) EqualIter(it iter.Seq[E]) bool { + return self.Equal(Collect(it)) +} + // Iter returns a [iter.Seq] that iterate over the keys in the set. func (self HashSet[E]) Iter() iter.Seq[E] { return func(yield func(E) bool) { @@ -240,6 +245,16 @@ func (self HashSet[E]) Iter() iter.Seq[E] { } } +func (self HashSet[E]) IterMut() iter.Seq[*SetItem[E]] { + return func(yield func(*SetItem[E]) bool) { + for key := range self.inner { + if !yield(&SetItem[E]{item: key, s: self}) { + break + } + } + } +} + func (self HashSet[E]) MarshalJSON() ([]byte, error) { return json.Marshal(self.inner) } @@ -247,3 +262,12 @@ func (self HashSet[E]) MarshalJSON() ([]byte, error) { func (self HashSet[E]) UnmarshalJSON(v []byte) error { return json.Unmarshal(v, &self.inner) } + +type SetItem[E comparable] struct { + item E + s HashSet[E] +} + +func (s *SetItem[E]) Remove() { s.s.Remove(s.item) } + +func (s *SetItem[E]) Value() E { return s.item } diff --git a/sets/set_test.go b/sets/set_test.go index 9369704..dbbb189 100644 --- a/sets/set_test.go +++ b/sets/set_test.go @@ -25,7 +25,7 @@ func (i item) Clone() item { return item{key: i.key} } func TestHashSet_Add(t *testing.T) { a := quicktest.New(t) - s := NewHashSet[int]() + s := FromSlice[int]() s.Add(1, 2) a.Assert(s.Contains(1), quicktest.IsTrue) a.Assert(s.Contains(2), quicktest.IsTrue) @@ -34,8 +34,8 @@ func TestHashSet_Add(t *testing.T) { func TestHashSet_AddAll(t *testing.T) { a := quicktest.New(t) - s := NewHashSet[int]() - s.AddAll(NewHashSet(1, 2)) + s := FromSlice[int]() + s.AddAll(FromSlice(1, 2)) a.Assert(s.Contains(1), quicktest.IsTrue) a.Assert(s.Contains(2), quicktest.IsTrue) a.Assert(s.Contains(3), quicktest.IsFalse) @@ -44,7 +44,7 @@ func TestHashSet_AddAll(t *testing.T) { func TestHashSet_Remove(t *testing.T) { a := quicktest.New(t) - s := NewHashSet[int]() + s := FromSlice[int]() s.Add(1) s.Add(2) s.Remove(1) @@ -53,22 +53,9 @@ func TestHashSet_Remove(t *testing.T) { a.Assert(s.Size(), quicktest.Equals, 1) } -func TestHashSet_RemoveBy(t *testing.T) { - a := quicktest.New(t) - s := NewHashSet[int]() - s.Add(1) - s.Add(2) - s.Add(3) - s.RemoveBy(func(i int) bool { return i == 1 }) - a.Assert(s.Contains(1), quicktest.IsFalse) - a.Assert(s.Contains(2), quicktest.IsTrue) - a.Assert(s.Contains(3), quicktest.IsTrue) - a.Assert(s.Size(), quicktest.Equals, 2) -} - func TestHashSet_Clear(t *testing.T) { a := quicktest.New(t) - s := NewHashSet[int]() + s := FromSlice[int]() s.Add(1) s.Add(2) s.Clear() @@ -79,7 +66,7 @@ func TestHashSet_Clear(t *testing.T) { func TestHashSet_Contains(t *testing.T) { a := quicktest.New(t) - s := NewHashSet[int]() + s := FromSlice[int]() s.Add(1) s.Add(2) a.Assert(s.Contains(1), quicktest.IsTrue) @@ -89,7 +76,7 @@ func TestHashSet_Contains(t *testing.T) { func TestHashSet_ContainsAll(t *testing.T) { a := quicktest.New(t) - s := NewHashSet[int]() + s := FromSlice[int]() s.Add(1) s.Add(2) s.Add(3) @@ -99,7 +86,7 @@ func TestHashSet_ContainsAll(t *testing.T) { func TestHashSet_ContainsAny(t *testing.T) { a := quicktest.New(t) - s := NewHashSet[int]() + s := FromSlice[int]() s.Add(1) s.Add(2) s.Add(3) @@ -110,7 +97,7 @@ func TestHashSet_ContainsAny(t *testing.T) { func TestHashSet_Size(t *testing.T) { a := quicktest.New(t) - s := NewHashSet[int]() + s := FromSlice[int]() s.Add(1) s.Add(2) s.Add(3) @@ -123,7 +110,7 @@ func TestHashSet_Size(t *testing.T) { func TestHashSet_IsEmpty(t *testing.T) { a := quicktest.New(t) - s := NewHashSet[int]() + s := FromSlice[int]() a.Assert(s.IsEmpty(), quicktest.IsTrue) s.Add(1) a.Assert(s.IsEmpty(), quicktest.IsFalse) @@ -131,7 +118,7 @@ func TestHashSet_IsEmpty(t *testing.T) { func TestHashSet_Clone(t *testing.T) { a := quicktest.New(t) - s1 := NewHashSet[int]() + s1 := FromSlice[int]() s1.Add(1) s1.Add(2) s1.Add(3) @@ -143,7 +130,7 @@ func TestHashSet_Clone(t *testing.T) { func TestHashSet_DeepCloneBy(t *testing.T) { a := quicktest.New(t) - s1 := NewHashSet[int]() + s1 := FromSlice[int]() s1.Add(1) s1.Add(2) s1.Add(3) @@ -155,11 +142,11 @@ func TestHashSet_DeepCloneBy(t *testing.T) { func TestHashSet_SupersetOf(t *testing.T) { a := quicktest.New(t) - s1 := NewHashSet[int]() + s1 := FromSlice[int]() s1.Add(1) s1.Add(2) s1.Add(3) - s2 := NewHashSet[int]() + s2 := FromSlice[int]() a.Assert(s1.SupersetOf(s2), quicktest.IsTrue) s2.Add(1) s2.Add(2) @@ -171,11 +158,11 @@ func TestHashSet_SupersetOf(t *testing.T) { func TestHashSet_SubsetOf(t *testing.T) { a := quicktest.New(t) - s1 := NewHashSet[int]() + s1 := FromSlice[int]() s1.Add(1) s1.Add(2) s1.Add(3) - s2 := NewHashSet[int]() + s2 := FromSlice[int]() a.Assert(s2.SubsetOf(s1), quicktest.IsTrue) s2.Add(1) s2.Add(2) @@ -187,11 +174,11 @@ func TestHashSet_SubsetOf(t *testing.T) { func TestHashSet_Union(t *testing.T) { a := quicktest.New(t) - s1 := NewHashSet[int]() + s1 := FromSlice[int]() s1.Add(1) s1.Add(2) s1.Add(3) - s2 := NewHashSet[int]() + s2 := FromSlice[int]() s2.Add(1) s2.Add(4) s2.Add(5) @@ -202,11 +189,11 @@ func TestHashSet_Union(t *testing.T) { func TestHashSet_Intersection(t *testing.T) { a := quicktest.New(t) - s1 := NewHashSet[int]() + s1 := FromSlice[int]() s1.Add(1) s1.Add(2) s1.Add(3) - s2 := NewHashSet[int]() + s2 := FromSlice[int]() s2.Add(1) s2.Add(4) s2.Add(5) @@ -217,11 +204,11 @@ func TestHashSet_Intersection(t *testing.T) { func TestHashSet_Difference(t *testing.T) { a := quicktest.New(t) - s1 := NewHashSet[int]() + s1 := FromSlice[int]() s1.Add(1) s1.Add(2) s1.Add(3) - s2 := NewHashSet[int]() + s2 := FromSlice[int]() s2.Add(1) s2.Add(4) s2.Add(5) @@ -232,11 +219,11 @@ func TestHashSet_Difference(t *testing.T) { func TestHashSet_SymmetricDifference(t *testing.T) { a := quicktest.New(t) - s1 := NewHashSet[int]() + s1 := FromSlice[int]() s1.Add(1) s1.Add(2) s1.Add(3) - s2 := NewHashSet[int]() + s2 := FromSlice[int]() s2.Add(1) s2.Add(4) s2.Add(5) @@ -247,11 +234,11 @@ func TestHashSet_SymmetricDifference(t *testing.T) { func TestHashSet_Equal(t *testing.T) { a := quicktest.New(t) - s1 := NewHashSet[int]() + s1 := FromSlice[int]() s1.Add(1) s1.Add(2) s1.Add(3) - s2 := NewHashSet[int]() + s2 := FromSlice[int]() s2.Add(1) s2.Add(2) s2.Add(3) @@ -262,14 +249,14 @@ func TestHashSet_Equal(t *testing.T) { func TestHashSet_Iter(t *testing.T) { a := quicktest.New(t) - s1 := NewHashSet(1, 2, 3, 4, 5) + s1 := FromSlice(1, 2, 3, 4, 5) a.Assert(iter.Size(s1.Iter()), quicktest.Equals, 5) } func TestHashSet_Marshal(t *testing.T) { a := quicktest.New(t) - s := NewHashSet(5, 1, 4, 2, 3, 1, 2, 3) + s := FromSlice(5, 1, 4, 2, 3, 1, 2, 3) b, err := json.Marshal(s) a.Assert(err, quicktest.IsNil) a.Logf("%s\n ", b) @@ -277,9 +264,20 @@ func TestHashSet_Marshal(t *testing.T) { func TestHashSet_UnmarshalJSON(t *testing.T) { a := quicktest.New(t) - s := NewHashSet[int]() + s := FromSlice[int]() err := json.Unmarshal([]byte(`{"1":{},"2":{},"3":{},"4":{},"5":{}}`), &s) a.Assert(err, quicktest.IsNil) a.Logf("%+v\n", s) - a.Assert(s.Equal(NewHashSet(1, 2, 3, 4, 5)), quicktest.IsTrue) + a.Assert(s.Equal(FromSlice(1, 2, 3, 4, 5)), quicktest.IsTrue) +} + +func TestIterMut(t *testing.T) { + s := FromSlice[int](1, 2, 3, 4) + s.IterMut().ForEach(func(s *SetItem[int]) { + if s.Value()%2 == 0 { + s.Remove() + } + }) + x := s.ToMap() + t.Logf("result is %+v\n", x) } diff --git a/slices/example_test.go b/slices/example_test.go index c16da72..5f071b8 100644 --- a/slices/example_test.go +++ b/slices/example_test.go @@ -13,7 +13,10 @@ type user struct { } func (u user) Compare(o user) int { - return cmp.Compare(u.Id, o.Id) + if x := cmp.Compare(u.Id, o.Id); x != 0 { + return x + } + return cmp.Compare(u.Name, o.Name) } func (u user) Clone() user { @@ -385,7 +388,7 @@ func ExampleGroupBy() { {Id: 2, Name: "Jack"}, {Id: 4, Name: "Bob"}, } - result := slices.GroupBy(slice, func(u user) (int64, user) { return u.Id, u }) + result := slices.GroupBy(slice, func(u user) int64 { return u.Id }) fmt.Println(result) // Output: // map[1:[{1 John}] 2:[{2 Jane} {2 Jack}] 4:[{4 Bob}]] @@ -503,7 +506,12 @@ func ExampleIntersectionBy() { {Id: 5, Name: "Bob"}, {Id: 6, Name: "Jack"}, } - result := slices.IntersectionBy(slice, slice2, func(a, b user) bool { return a.Id == b.Id && a.Name == b.Name }) + result := slices.IntersectionBy(slice, slice2, func(a, b user) int { + if x := cmp.Compare(a.Id, b.Id); x != 0 { + return x + } + return cmp.Compare(a.Name, b.Name) + }) fmt.Println(result) // Output: // [{1 John} {2 Jane}] @@ -522,7 +530,7 @@ func ExampleDifferenceBy() { {Id: 5, Name: "Bob"}, {Id: 6, Name: "Jack"}, } - result := slices.DifferenceBy(slice, slice2, func(a, b user) bool { return a.Id == b.Id && a.Name == b.Name }) + result := slices.DifferenceBy(slice, slice2, func(a, b user) int { return user.Compare(a, b) }) fmt.Println(result) // Output: // [{3 Jack} {4 Bob}] diff --git a/slices/slice.go b/slices/slice.go index 91d5616..116a00c 100644 --- a/slices/slice.go +++ b/slices/slice.go @@ -6,6 +6,7 @@ import ( "github.com/go-board/std/clone" "github.com/go-board/std/cmp" + "github.com/go-board/std/collections/ordered" "github.com/go-board/std/core" "github.com/go-board/std/iter" "github.com/go-board/std/operator" @@ -13,7 +14,12 @@ import ( "github.com/go-board/std/result" ) -func ForwardSeq[E any, S ~[]E](s S) iter.Seq[E] { +// Forward create a Seq in order. +// +// Example: +// +// slices.Forward([]int{1,2,3}) => seq: 1,2,3 +func Forward[E any, S ~[]E](s S) iter.Seq[E] { return func(yield func(E) bool) { for _, x := range s { if !yield(x) { @@ -23,7 +29,7 @@ func ForwardSeq[E any, S ~[]E](s S) iter.Seq[E] { } } -func BackwardSeq[E any, S ~[]E](s S) iter.Seq[E] { +func Backward[E any, S ~[]E](s S) iter.Seq[E] { return func(yield func(E) bool) { for i := len(s) - 1; i >= 0; i-- { if !yield(s[i]) { @@ -33,6 +39,11 @@ func BackwardSeq[E any, S ~[]E](s S) iter.Seq[E] { } } +// Collect all elements in [Seq] and return a slice. +// +// Example: +// +// slices.Collect(seq(1,2,3)) => []int{1,2,3} func Collect[E any](s iter.Seq[E]) []E { rs := make([]E, 0) s(func(x E) bool { @@ -42,17 +53,57 @@ func Collect[E any](s iter.Seq[E]) []E { return rs } +// CollectInto collect all elements in [Seq] to a pre-allocated slice. +// +// Example: +// +// ints := make([]int, 0, len(items)) +// slices.CollectInto(seq(1,2,3), &ints) => ints: []int{1,2,3} +func CollectInto[E any, S ~[]E](s iter.Seq[E], slice *S) { + iter.CollectFunc(s, func(e E) bool { + *slice = append(*slice, e) + return true + }) +} + // All returns true if all elements in the given slice satisfy the given predicate. +// +// Example: +// +// slices.All([]int{1,2,3}, func(x int) bool { return x > 0 }) => true +// slices.All([]int{1,2,3}, func(x int) bool { return x > 2 }) => false func All[T any, S ~[]T](slice S, f func(T) bool) bool { - return iter.All(ForwardSeq(slice), f) + return Forward(slice).All(f) } // Any returns true if any element in the given slice satisfies the given predicate. +// +// Example: +// +// slices.Any([]int{1,2,3}, func(x int) bool { return x > 2 }) => true +// slices.Any([]int{1,2,3}, func(x int) bool { return x > 6 }) => false func Any[T any, S ~[]T](slice S, f func(T) bool) bool { - return iter.Any(ForwardSeq(slice), f) + return Forward(slice).Any(f) +} + +// Concat all slices into a new one. +// +// Example: +// +// slices.Concat([]int{1,2,3}, []int{4,5,6}, []int{7,8,9}) => []int{1,2,3,4,5,6,7,8,9} +func Concat[T any, S ~[]T](slices ...S) S { + n := Fold(slices, 0, func(i int, s S) int { return i + len(s) }) + s := make(S, 0, n) + s = Fold(slices, s, func(s S, s2 S) S { return append(s, s2...) }) + return s } // Chunk returns a new slice with the given slice split into smaller slices of the given size. +// +// Example: +// +// slices.Chunk(seq(1,2,3,4,5,6,7,8,9), 3) => [][]int{{1,2,3}, {4,5,6}, {7,8,9}} +// slices.Chunk(seq(1,2,3,4,5,6,7), 3) => [][]int{{1,2,3}, {4,5,6}, {7}} func Chunk[T any, S ~[]T](slice S, chunk int) []S { size := len(slice) res := make([]S, 0, len(slice)/chunk+1) @@ -71,8 +122,8 @@ func Chunk[T any, S ~[]T](slice S, chunk int) []S { } // Clone returns a new slice with the same elements as the given slice. -func Clone[T any, S ~[]T](slice []T) []T { - return Map(slice, func(t T) T { return t }) +func Clone[T any, S ~[]T](slice S) S { + return append(S(nil), slice...) } // DeepClone returns a new slice with the cloned elements. @@ -86,6 +137,11 @@ func DeepCloneBy[T any, S ~[]T](slice S, clone func(T) T) S { } // Difference returns a new slice with the elements that are in the first slice but not in the second. +// +// Example: +// +// slices.Difference([]int{1,2,3}, []int{3,4,5}) => []int{1,2} +// slices.Difference([]int{1,2,3}, []int{1,2,3,4,5}) => []int{} func Difference[T comparable, S ~[]T](lhs S, rhs S) S { s := ToHashSet[T](rhs) x := make(S, 0) @@ -98,24 +154,21 @@ func Difference[T comparable, S ~[]T](lhs S, rhs S) S { } // DifferenceBy returns a new slice with the elements that are in the first slice but not in the second by the given function. -func DifferenceBy[T any, S1 ~[]T, S2 ~[]T](lhs S1, rhs S2, eq func(T, T) bool) []T { - res := make([]T, 0, len(lhs)) - for _, v := range lhs { - // TODO: optimize use O(1) lookup - if !ContainsBy(rhs, func(t T) bool { return eq(v, t) }) { - res = append(res, v) - } - } - return res +func DifferenceBy[T any, S ~[]T](lhs S, rhs S, cmp func(T, T) int) S { + l := ordered.NewSet(cmp) + l.InsertIter(Forward(lhs)) + r := ordered.NewSet(cmp) + r.InsertIter(Forward(rhs)) + return Collect(l.Difference(r).AscendIter()) } // Distinct returns a new slice with the given slice without duplicates. -func Distinct[T comparable, S ~[]T](slice S) []T { - return DistinctBy(slice, func(x T) T { return x }) +func Distinct[T comparable, S ~[]T](slice S) S { + return Collect(iter.Distinct(Forward(slice))) } // DistinctBy returns a new slice with the distinct elements of the given slice by the given function. -func DistinctBy[T any, K comparable, S ~[]T](slice S, key func(T) K) []T { +func DistinctBy[T any, K comparable, S ~[]T](slice S, key func(T) K) S { m := make(map[K]T) for _, v := range slice { m[key(v)] = v @@ -133,7 +186,7 @@ func DistinctBy[T any, K comparable, S ~[]T](slice S, key func(T) K) []T { // Filter returns a new slice with all elements that satisfy the given predicate. func Filter[T any, S ~[]T](slice S, f func(T) bool) S { - return Collect(iter.Filter(ForwardSeq(slice), f)) + return Collect(Forward(slice).Filter(f)) } // FilterIndexed returns a new slice with all elements that satisfy the given predicate. @@ -148,8 +201,8 @@ func FilterIndexed[T any, S ~[]T](slice S, f func(T, int) bool) S { } // Flatten returns a new slice with all elements in the given slice and all elements in all sub-slices. -func Flatten[T any, S ~[]T](slice []S) []T { - return FlattenBy(slice, func(t S) []T { return t }) +func Flatten[T any, S ~[]T, X ~[]S](slice X) S { + return FlatMap(slice, func(t S) []T { return t }) } // FlattenBy returns a new slice with all elements in the given slice and all elements in the given slices. @@ -171,60 +224,57 @@ func FlatMap[T, E any, S ~[]T](slice S, f func(T) []E) []E { // Returns the final accum value or initial value if the slice is empty. // If error occurred, return error early. func TryFold[T, A any, S ~[]T](slice S, initial A, accumulator func(A, T) (A, error)) (res A, err error) { - return iter.TryFold(ForwardSeq(slice), initial, accumulator) + return iter.TryFold(Forward(slice), initial, accumulator) } // Fold accumulates value starting with initial value and applying accumulator from left to right to current accum value and each element. // Returns the final accum value or initial value if the slice is empty. func Fold[T, A any, S ~[]T](slice S, initial A, accumulator func(A, T) A) A { - return iter.Fold(ForwardSeq(slice), initial, accumulator) + return iter.Fold(Forward(slice), initial, accumulator) } func TryFoldRight[T, A any, S ~[]T](slice S, initial A, accumulator func(A, T) (A, error)) (res A, err error) { - return iter.TryFold(BackwardSeq(slice), initial, accumulator) + return iter.TryFold(Backward(slice), initial, accumulator) } // FoldRight accumulates value starting with initial value and applying accumulator from right to left to current accum value and each element. // Returns the final accum value or initial value if the slice is empty. func FoldRight[T, A any, S ~[]T](slice S, initial A, accumulator func(A, T) A) A { - return iter.Fold(BackwardSeq(slice), initial, accumulator) + return iter.Fold(Backward(slice), initial, accumulator) } // ForEach iterates over the given slice and calls the given function for each element. func ForEach[T any, S ~[]T](slice S, f func(T)) { - iter.ForEach(ForwardSeq(slice), f) + Forward(slice).ForEach(f) } // ForEachIndexed iterates over the given slice and calls the given function for each element. func ForEachIndexed[T any, S ~[]T](slice S, f func(T, int)) { - for i, v := range slice { - f(v, i) - } + iter.Enumerate(Forward(slice)).ForEach(func(t iter.Tuple[int, T]) { + f(t.Right, t.Left) + }) } // GroupBy returns a new map with the given slice split into smaller slices of the given size. -func GroupBy[T any, TKey comparable, TValue any, S ~[]T](slice S, group func(T) (TKey, TValue)) map[TKey][]TValue { - res := make(map[TKey][]TValue) +func GroupBy[T any, TKey comparable, S ~[]T](slice S, f func(T) TKey) map[TKey]S { + res := make(map[TKey]S) for _, v := range slice { - key, value := group(v) - res[key] = append(res[key], value) + key := f(v) + res[key] = append(res[key], v) } return res } // IntersectionBy returns a new slice with the elements that are in both given slices by the given function. -func IntersectionBy[T any, S1 ~[]T, S2 ~[]T](lhs S1, rhs S2, eq func(T, T) bool) []T { - res := make([]T, 0, len(lhs)) - for _, v := range lhs { - // TODO: optimize use O(1) lookup - if ContainsBy(rhs, func(t T) bool { return eq(t, v) }) { - res = append(res, v) - } - } - return res +func IntersectionBy[T any, S ~[]T](lhs S, rhs S, cmp func(T, T) int) S { + l := ordered.NewSet(cmp) + l.InsertIter(Forward(lhs)) + r := ordered.NewSet(cmp) + r.InsertIter(Forward(rhs)) + return Collect(l.Intersection(r).AscendIter()) } -func Intersection[T comparable](lhs []T, rhs []T) []T { +func Intersection[T comparable, S ~[]T](lhs S, rhs S) S { s := ToHashSet(lhs) res := make([]T, 0) for _, x := range rhs { @@ -237,13 +287,13 @@ func Intersection[T comparable](lhs []T, rhs []T) []T { // LastIndex returns the index of the last element in the given slice that same with the given element. func LastIndex[T comparable, S ~[]T](slice S, v T) optional.Optional[int] { - return LastIndexBy(slice, v, operator.Eq[T]) + return LastIndexBy(slice, func(t T) bool { return t == v }) } // LastIndexBy returns the index of the last element in the given slice that satisfies the given predicate. -func LastIndexBy[T any, S ~[]T](slice S, v T, eq func(T, T) bool) optional.Optional[int] { +func LastIndexBy[T any, S ~[]T](slice S, f func(T) bool) optional.Optional[int] { for i := len(slice) - 1; i >= 0; i-- { - if eq(v, slice[i]) { + if f(slice[i]) { return optional.Some(i) } } @@ -264,7 +314,7 @@ func TryMap[T, U any, S ~[]T](slice S, f func(T) (U, error)) ([]U, error) { // Map returns a new slice with the results of applying the given function to each element in the given slice. func Map[T, U any, S ~[]T](slice S, f func(T) U) []U { - return Collect(iter.Map(ForwardSeq(slice), f)) + return Collect(iter.Map(Forward(slice), f)) } func TryMapIndexed[T, U any, S ~[]T](slice S, f func(T, int) (U, error)) ([]U, error) { @@ -290,34 +340,32 @@ func MapIndexed[T, U any, S ~[]T](slice S, f func(T, int) U) []U { // Max returns the maximum element in the given slice. func Max[T cmp.Ordered, S ~[]T](slice S) optional.Optional[T] { - return optional.FromPair(iter.Max(ForwardSeq(slice))) + return optional.FromPair(iter.Max(Forward(slice))) } // MaxBy returns the maximum element in the given slice that satisfies the given function. func MaxBy[T any, S ~[]T](slice S, f func(T, T) int) optional.Optional[T] { - return optional.FromPair(iter.MaxFunc(ForwardSeq(slice), f)) + return optional.FromPair(iter.MaxFunc(Forward(slice), f)) } func MaxByKey[T any, K cmp.Ordered, S ~[]T](slice S, keyFn func(T) K) optional.Optional[T] { - return optional.FromPair(iter.MaxFunc(ForwardSeq(slice), func(x, y T) int { - return cmp.Compare(keyFn(x), keyFn(y)) - })) + f := func(x, y T) int { return cmp.Compare(keyFn(x), keyFn(y)) } + return MaxBy(slice, f) } // Min returns the minimum element in the given slice. func Min[T core.Ordered, S ~[]T](slice S) optional.Optional[T] { - return optional.FromPair(iter.Min(ForwardSeq(slice))) + return optional.FromPair(iter.Min(Forward(slice))) } // MinBy returns the minimum element in the given slice that satisfies the given function. func MinBy[T any, S ~[]T](slice S, f func(T, T) int) optional.Optional[T] { - return optional.FromPair(iter.MinFunc(ForwardSeq(slice), f)) + return optional.FromPair(iter.MinFunc(Forward(slice), f)) } func MinByKey[T any, K cmp.Ordered, S ~[]T](slice S, keyFn func(T) K) optional.Optional[T] { - return optional.FromPair(iter.MinFunc(ForwardSeq(slice), func(x, y T) int { - return cmp.Compare(keyFn(x), keyFn(y)) - })) + f := func(x, y T) int { return cmp.Compare(keyFn(x), keyFn(y)) } + return MinBy(slice, f) } // None returns true if no element in the given slice satisfies the given predicate. @@ -348,9 +396,9 @@ func Nth[T any, S ~[]T](slice S, n int) optional.Optional[T] { // // Partition([]int{1, 2, 3}, func(s int) bool { return s % 2 == 0 }) // returns: ([2], [1, 3]) -func Partition[T any, S ~[]T](slice S, f func(T) bool) ([]T, []T) { - lhs := make([]T, 0) - rhs := make([]T, 0) +func Partition[T any, S ~[]T](slice S, f func(T) bool) (S, S) { + lhs := make(S, 0) + rhs := make(S, 0) for _, e := range slice { if f(e) { lhs = append(lhs, e) @@ -363,24 +411,30 @@ func Partition[T any, S ~[]T](slice S, f func(T) bool) ([]T, []T) { // Reduce returns the result of applying the given function to each element in the given slice. func Reduce[T any, S ~[]T](slice S, f func(T, T) T) optional.Optional[T] { - return optional.FromPair(iter.Reduce(ForwardSeq(slice), f)) + return optional.FromPair(iter.Reduce(Forward(slice), f)) } // ReduceRight returns the result of applying the given function to each element in the given slice. func ReduceRight[T any, S ~[]T](slice S, f func(T, T) T) optional.Optional[T] { - return optional.FromPair(iter.Reduce(BackwardSeq(slice), f)) + return optional.FromPair(iter.Reduce(Backward(slice), f)) } // Reverse returns a new slice with the elements in the given slice in reverse order. -func Reverse[T any, S ~[]T](slice S) { +// +// Example: +// +// slices.Reverse([]int{1,2,3}) => []int{3,2,1} +func Reverse[T any, S ~[]T](slice S) S { for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 { slice[i], slice[j] = slice[j], slice[i] } + return slice } // Shuffle the given slice in-place. -func Shuffle[T any, S ~[]T](slice S) { +func Shuffle[T any, S ~[]T](slice S) S { rand.Shuffle(len(slice), func(i, j int) { slice[i], slice[j] = slice[j], slice[i] }) + return slice } // Single returns the single element, @@ -442,7 +496,12 @@ func Last[T any, S ~[]T](slice S) optional.Optional[T] { } // SpliceFirst return first element and rest if len > 0, else return (None, []T) -func SpliceFirst[T any, S ~[]T](slice S) (optional.Optional[T], []T) { +// +// Example: +// +// slices.SpliceFirst([]int{1,2,3}) => Some(1), []int{2,3} +// slices.SpliceFirst([]int{}) => None, []int{} +func SpliceFirst[T any, S ~[]T](slice S) (optional.Optional[T], S) { if len(slice) > 0 { return optional.Some(slice[0]), slice[1:] } @@ -450,14 +509,19 @@ func SpliceFirst[T any, S ~[]T](slice S) (optional.Optional[T], []T) { } // SpliceLast return last element and rest if len > 0, else return (None, []T) -func SpliceLast[T any, S ~[]T](slice S) (optional.Optional[T], []T) { +// +// Example: +// +// slices.SpliceLast([]int{1,2,3}) => Some(1), []int{1,2} +// slices.SpliceLast([]int{}) => None, []int{} +func SpliceLast[T any, S ~[]T](slice S) (optional.Optional[T], S) { if len(slice) > 0 { return optional.Some(slice[len(slice)-1]), slice[:len(slice)-1] } return optional.None[T](), slice } -// FirstNonZero returns first non zero value +// FirstNonZero returns first non-zero value // // zero value are: // @@ -469,11 +533,6 @@ func SpliceLast[T any, S ~[]T](slice S) (optional.Optional[T], []T) { // interface: nil // chan/map/slice: nil func FirstNonZero[T comparable, S ~[]T](slice S) T { - var zero T - for _, v := range slice { - if zero != v { - return v - } - } - return zero + first, _ := iter.FilterZero(Forward(slice)).First(func(t T) bool { return true }) + return first }