mirror of
https://github.com/samber/lo.git
synced 2026-04-22 23:47:11 +08:00
43cef1f439
* lint: pin golangci-lint version * lint: fix issues triggered by go1.23 upgrade * feat: new iter package * lint: fix linter issues * fix: restore go1.18 * fix: rename package to "it" * feat: assign multiple sequences of maps * fix: panic in DropRight if n = 0 * docs: fix incorrect non-iter helper references * feat: implement Invert helper * feat: helpers for creating and checking empty sequences * feat: implement Reverse helper * feat: implement ReduceRight helper * feat: implement Shuffle helper * feat: implement Sample* helpers * refactor: rename helpers with Seq convention * feat: implement SeqToChannel2 helper * feat: implement HasPrefix/HasSuffix helpers * chore: port recent changes * perf: only iterate collection once in Every * refactor: reduce dupe code by reusing helpers internally * perf: reuse internal Mode slice * feat: implement Length helper * chore: duplicate unit tests for *I helpers * fix: omit duplicates in second Intersect list * feat: intersect more than 2 sequences * feat: implement Drain helper * feat: implement Seq/Seq2 conversion helpers * refactor: rename *Right* to *Last* * chore: minor cleanup * refactor: consistent predicate/transform parameter names * perf: abort Slice/Subset once upper bound reached * refactor: rename IsSortedByKey to IsSortedBy * refactor: reuse more helpers internally * feat: implement Cut* helpers * feat: implement Trim* helpers * perf: reduce allocations * docs: describe iteration and allocation expectations * Update .github/workflows/lint.yml --------- Co-authored-by: Samuel Berthe <dev@samuel-berthe.fr>
207 lines
6.7 KiB
Go
207 lines
6.7 KiB
Go
//go:build go1.23
|
|
|
|
package it
|
|
|
|
import (
|
|
"iter"
|
|
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
// Contains returns true if an element is present in a collection.
|
|
// Will iterate through the entire sequence if element is not found.
|
|
func Contains[T comparable](collection iter.Seq[T], element T) bool {
|
|
return ContainsBy(collection, func(item T) bool { return item == element })
|
|
}
|
|
|
|
// ContainsBy returns true if predicate function return true.
|
|
// Will iterate through the entire sequence if predicate never returns true.
|
|
func ContainsBy[T any](collection iter.Seq[T], predicate func(item T) bool) bool {
|
|
return IsNotEmpty(Filter(collection, predicate))
|
|
}
|
|
|
|
// Every returns true if all elements of a subset are contained in a collection or if the subset is empty.
|
|
// Will iterate through the entire sequence if subset elements always match.
|
|
func Every[T comparable](collection iter.Seq[T], subset ...T) bool {
|
|
if len(subset) == 0 {
|
|
return true
|
|
}
|
|
|
|
set := lo.Keyify(subset)
|
|
for item := range collection {
|
|
if _, ok := set[item]; ok {
|
|
delete(set, item)
|
|
if len(set) == 0 {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// EveryBy returns true if the predicate returns true for all elements in the collection or if the collection is empty.
|
|
// Will iterate through the entire sequence if predicate never returns false.
|
|
func EveryBy[T any](collection iter.Seq[T], predicate func(item T) bool) bool {
|
|
return IsEmpty(Reject(collection, predicate))
|
|
}
|
|
|
|
// Some returns true if at least 1 element of a subset is contained in a collection.
|
|
// If the subset is empty Some returns false.
|
|
// Will iterate through the entire sequence if subset elements never match.
|
|
func Some[T comparable](collection iter.Seq[T], subset ...T) bool {
|
|
return SomeBy(collection, func(item T) bool { return lo.Contains(subset, item) })
|
|
}
|
|
|
|
// SomeBy returns true if the predicate returns true for any of the elements in the collection.
|
|
// If the collection is empty SomeBy returns false.
|
|
// Will iterate through the entire sequence if predicate never returns true.
|
|
func SomeBy[T any](collection iter.Seq[T], predicate func(item T) bool) bool {
|
|
return IsNotEmpty(Filter(collection, predicate))
|
|
}
|
|
|
|
// None returns true if no element of a subset is contained in a collection or if the subset is empty.
|
|
// Will iterate through the entire sequence if subset elements never match.
|
|
func None[T comparable](collection iter.Seq[T], subset ...T) bool {
|
|
return NoneBy(collection, func(item T) bool { return lo.Contains(subset, item) })
|
|
}
|
|
|
|
// NoneBy returns true if the predicate returns true for none of the elements in the collection or if the collection is empty.
|
|
// Will iterate through the entire sequence if predicate never returns true.
|
|
func NoneBy[T any](collection iter.Seq[T], predicate func(item T) bool) bool {
|
|
return IsEmpty(Filter(collection, predicate))
|
|
}
|
|
|
|
// Intersect returns the intersection between given collections.
|
|
// Will allocate a map large enough to hold all distinct elements.
|
|
// Long heterogeneous input sequences can cause excessive memory usage.
|
|
func Intersect[T comparable, I ~func(func(T) bool)](lists ...I) I { //nolint:gocyclo
|
|
if len(lists) == 0 {
|
|
return I(Empty[T]())
|
|
}
|
|
|
|
if len(lists) == 1 {
|
|
return lists[0]
|
|
}
|
|
|
|
return func(yield func(T) bool) {
|
|
seen := make(map[T]bool)
|
|
|
|
for i := len(lists) - 1; i >= 0; i-- {
|
|
if i == len(lists)-1 {
|
|
for item := range lists[i] {
|
|
seen[item] = true
|
|
}
|
|
continue
|
|
}
|
|
|
|
if i == 0 {
|
|
for item := range lists[0] {
|
|
if _, ok := seen[item]; ok {
|
|
if !yield(item) {
|
|
return
|
|
}
|
|
delete(seen, item)
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
|
|
for k := range seen {
|
|
seen[k] = false
|
|
}
|
|
|
|
for item := range lists[i] {
|
|
if _, ok := seen[item]; ok {
|
|
seen[item] = true
|
|
}
|
|
}
|
|
|
|
for k, v := range seen {
|
|
if !v {
|
|
delete(seen, k)
|
|
}
|
|
}
|
|
|
|
if len(seen) == 0 {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Union returns all distinct elements from given collections.
|
|
// Will allocate a map large enough to hold all distinct elements.
|
|
// Long heterogeneous input sequences can cause excessive memory usage.
|
|
func Union[T comparable, I ~func(func(T) bool)](lists ...I) I {
|
|
return func(yield func(T) bool) {
|
|
seen := make(map[T]struct{})
|
|
|
|
for i := range lists {
|
|
for item := range lists[i] {
|
|
if _, ok := seen[item]; !ok {
|
|
if !yield(item) {
|
|
return
|
|
}
|
|
seen[item] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Without returns a sequence excluding all given values.
|
|
// Will allocate a map large enough to hold all distinct excludes.
|
|
func Without[T comparable, I ~func(func(T) bool)](collection I, exclude ...T) I {
|
|
return WithoutBy(collection, func(item T) T { return item }, exclude...)
|
|
}
|
|
|
|
// WithoutBy filters a sequence by excluding elements whose extracted keys match any in the exclude list.
|
|
// Returns a sequence containing only the elements whose keys are not in the exclude list.
|
|
// Will allocate a map large enough to hold all distinct excludes.
|
|
func WithoutBy[T any, K comparable, I ~func(func(T) bool)](collection I, transform func(item T) K, exclude ...K) I {
|
|
set := lo.Keyify(exclude)
|
|
return Reject(collection, func(item T) bool { return lo.HasKey(set, transform(item)) })
|
|
}
|
|
|
|
// WithoutNth returns a sequence excluding the nth value.
|
|
// Will allocate a map large enough to hold all distinct nths.
|
|
func WithoutNth[T comparable, I ~func(func(T) bool)](collection I, nths ...int) I {
|
|
set := lo.Keyify(nths)
|
|
return RejectI(collection, func(_ T, index int) bool { return lo.HasKey(set, index) })
|
|
}
|
|
|
|
// ElementsMatch returns true if lists contain the same set of elements (including empty set).
|
|
// If there are duplicate elements, the number of occurrences in each list should match.
|
|
// The order of elements is not checked.
|
|
// Will iterate through each sequence before returning and allocate a map large enough to hold all distinct elements.
|
|
// Long heterogeneous input sequences can cause excessive memory usage.
|
|
func ElementsMatch[T comparable](list1, list2 iter.Seq[T]) bool {
|
|
return ElementsMatchBy(list1, list2, func(item T) T { return item })
|
|
}
|
|
|
|
// ElementsMatchBy returns true if lists contain the same set of elements' keys (including empty set).
|
|
// If there are duplicate keys, the number of occurrences in each list should match.
|
|
// The order of elements is not checked.
|
|
// Will iterate through each sequence before returning and allocate a map large enough to hold all distinct transformed elements.
|
|
// Long heterogeneous input sequences can cause excessive memory usage.
|
|
func ElementsMatchBy[T any, K comparable](list1, list2 iter.Seq[T], transform func(item T) K) bool {
|
|
counters := make(map[K]int)
|
|
|
|
for item := range list1 {
|
|
counters[transform(item)]++
|
|
}
|
|
|
|
for item := range list2 {
|
|
counters[transform(item)]--
|
|
}
|
|
|
|
for _, count := range counters {
|
|
if count != 0 {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|