Golang implementation of generic lazily iterated sequences.
Requires Go 1.18+.
import "github.com/orangootan/go-seq/pkg/seq"
All examples use unqualified import as if:
import . "github.com/orangootan/go-seq/pkg/seq"
Seq[T] is the main interface declaring methods for working with sequences. Some functions are implemented as standalone functions (not methods of this interface) due to limitations of Go generics implementation.
FromSlice() returns a sequence of items from the given slice.
See further examples.
Iterator() returns iterator object of a sequence.
Next() advances iterator to the next item of the iterated sequence. Returns true if successfully advanced or false if passed the end of the sequence.
Current() returns pointer to the current item of the iterated sequence.
s := []int{1, 2, 3}
it := FromSlice(s).Iterator()
for it.Next() {
fmt.Println(*it.Current())
}
// Output:
// 1
// 2
// 3
AsSlice() collects all sequence items into newly created slice.
See further examples.
FromSliceReversed() returns a sequence of items from the given slice in inverted order.
s := FromSliceReversed([]int{1, 2, 3, 4})
fmt.Println(s.AsSlice())
// Output:
// [4 3 2 1]
FromChan() returns a sequence of items received from a channel until the channel is closed.
ch := make(chan int)
go func() {
ch <- 1
ch <- 2
ch <- 3
close(ch)
}()
fmt.Println(FromChan(ch).AsSlice())
// Output:
// [1 2 3]
Generate() returns an infinite sequence applying a mapping function to item indices.
s := Generate(func(i int) int { return i * i })
fmt.Println(s.Take(6).AsSlice())
// Output:
// [0 1 4 9 16 25]
Iterate() returns an infinite sequence consisting of seed, fn(seed), fn(fn(seed)), etc.
s := Iterate(2, func(i int) int { return 2 * i })
fmt.Println(s.Take(8).AsSlice())
// Output:
// [2 4 8 16 32 64 128 256]
Cycle() returns an infinite sequence by cycling items from the given slice.
s := []int{1, 2, 3}
r := Cycle(s).Take(9)
fmt.Println(r.AsSlice())
// Output:
// [1 2 3 1 2 3 1 2 3]
Repeat() returns an infinite sequence repeating the given item.
s := Repeat(1).Take(5)
fmt.Println(s.AsSlice())
// Output:
// [1 1 1 1 1]
// Single returns a sequence containing exactly one specified item.
s := Single(3)
fmt.Println(s.AsSlice())
fmt.Println(s.Count())
// Output:
// [3]
// 1
Empty() returns empty sequence.
fmt.Println(Empty[int]().Count())
fmt.Println(Empty[string]().AsSlice())
fmt.Println(Empty[int]().IsEmpty())
it := Empty[int]().Iterator()
fmt.Println(it.Next(), it.Current())
// Output:
// 0
// []
// true
// false <nil>
IsEmpty() determines whether a sequence is empty.
s1 := []int{1, 2, 3}
var s2 []int
fmt.Println(FromSlice(s1).IsEmpty())
fmt.Println(FromSlice(s2).IsEmpty())
fmt.Println(Empty[int]().IsEmpty())
// Output:
// false
// true
// true
Equal() determines whether two sequences have equal length and equal corresponding items. This function works only with sequences of comparable items.
s1 := FromSlice([]int{1, 2, 3, 4})
s2 := FromSlice([]int{1, 2, 3})
s3 := FromSlice([]int{0, 1, 2})
s4 := FromSlice([]int{0, 1, 2})
fmt.Println(Equal(s1, s2))
fmt.Println(Equal(s2, s3))
fmt.Println(Equal(s3, s4))
// Output:
// false
// false
// true
Concat() concatenates two sequences.
s1 := FromSlice([]int{1, 2, 3, 4})
s2 := FromSlice([]int{5, 6, 7})
r := s1.Concat(s2)
fmt.Println(r.AsSlice())
// Output:
// [1 2 3 4 5 6 7]
Cycled() repeats a sequence infinitely.
s := []int{1, 2, 3}
r := FromSlice(s).Cycled().Take(10)
fmt.Println(r.AsSlice())
// Output:
// [1 2 3 1 2 3 1 2 3 1]
Filter() filters a sequence of items based on the given predicate.
Where() is an alias for Filter(). Use whatever name you like.
isEven := func(i int) bool { return i%2 == 0 }
s := []int{1, 2, 3, 4, 5, 6}
r := FromSlice(s).Filter(isEven)
fmt.Println(r.AsSlice())
// Output:
// [2 4 6]
Reverse() inverts the order of items in a sequence.
s := []int{1, 2, 3, 4, 5, 6}
r := FromSlice(s).Reverse()
fmt.Println(r.AsSlice())
// Output:
// [6 5 4 3 2 1]
Skip() bypasses the given number of items in a sequence and then returns the remaining items.
s := []int{1, 2, 3, 4, 5, 6}
r := FromSlice(s).Skip(3)
fmt.Println(r.AsSlice())
// Output:
// [4 5 6]
SkipWhile() bypasses items in a sequence as long as the given condition is true and then returns the remaining items.
lessThan5 := func(i int) bool { return i < 5 }
s1 := []int{1, 2, 3, 4, 5, 6, 1}
s2 := []int{1, 2, 3}
r1 := FromSlice(s1).SkipWhile(lessThan5)
r2 := FromSlice(s2).SkipWhile(lessThan5)
fmt.Println(r1.AsSlice())
fmt.Println(r2.AsSlice())
// Output:
// [5 6 1]
// []
Take() returns the given number of items from the start of a sequence.
s := []int{1, 2, 3, 4, 5, 6}
r := FromSlice(s).Take(4)
fmt.Println(r.AsSlice())
// Output:
// [1 2 3 4]
TakeWhile() returns items from a sequence as long as the given condition is true, and then skips the remaining items.
lessThan5 := func(i int) bool { return i < 5 }
s := []int{1, 2, 3, 4, 5, 6, 1}
r := FromSlice(s).TakeWhile(lessThan5)
fmt.Println(r.AsSlice())
// Output:
// [1 2 3 4]
Interleave() returns a sequence that interleaves items from two sequences.
s1 := FromSlice([]int{1, 2, 3, 4})
s2 := FromSlice([]int{4, 5, 6})
r := s1.Interleave(s2)
fmt.Println(r.AsSlice())
// Output:
// [1 4 2 5 3 6 4]
Map() projects items of a sequence using the given function.
Select() is an alias for Map(). Use whatever name you like.
s := FromSlice([]int{1, 2, 3, 4, 5, 6})
r := Map(s, func(i int) int { return i * i })
fmt.Println(r.AsSlice())
// Output:
// [1 4 9 16 25 36]
Zip() combines items of two sequences pairwise using a projection function.
s1 := FromSlice([]int{1, 2, 3})
s2 := FromSlice([]int{4, 5, 6, 7})
r := Zip(s1, s2, func(u int, v int) [2]int {
return [2]int{u, v}
})
fmt.Println(r.AsSlice())
// Output:
// [[1 4] [2 5] [3 6]]
Zip3() is same as Zip but combines three sequences.
s1 := FromSlice([]int{1, 2, 3})
s2 := FromSlice([]int{4, 5, 6})
s3 := FromSlice([]int{7, 8, 9, 10})
r := Zip3(s1, s2, s3, func(u int, v int, w int) [3]int {
return [3]int{u, v, w}
})
fmt.Println(r.AsSlice())
// Output:
// [[1 4 7] [2 5 8] [3 6 9]]
All() determines whether all items of a sequence satisfy the given condition.
isEven := func(i int) bool { return i%2 == 0 }
s1 := []int{2, 4, 6, 8, 10}
s2 := []int{2, 4, 6, 8, 11}
fmt.Println(FromSlice(s1).All(isEven))
fmt.Println(FromSlice(s2).All(isEven))
// Output:
// true
// false
Any() determines whether any item of a sequence satisfies the given condition.
isEven := func(i int) bool { return i%2 == 0 }
s1 := []int{1, 3, 5, 7, 10}
s2 := []int{1, 3, 5, 7, 9}
fmt.Println(FromSlice(s1).Any(isEven))
fmt.Println(FromSlice(s2).Any(isEven))
// Output:
// true
// false
Chunk() splits a sequence into chunks of the given size and returns sequence of slices.
s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
r := Chunk(3, FromSlice(s))
fmt.Println(r.AsSlice())
// Output:
// [[1 2 3] [4 5 6] [7 8 9] [10 11]]
Contains() determines whether a sequence contains the given item. This function works only with sequences of comparable items.
s := FromSlice([]int{1, 2, 3, 4, 5})
fmt.Println(Contains(3, s))
fmt.Println(Contains(6, s))
// Output:
// true
// false
Count() counts number of items in a sequence.
s1 := []int{1, 2, 3, 4, 5}
var s2 []int
fmt.Println(FromSlice(s1).Count())
fmt.Println(FromSlice(s2).Count())
// Output:
// 5
// 0
Count() counts number of items in a sequence. You can specify an integer type for result.
s := []string{"Huey", "Dewey", "Louie"}
c := Count[string, uint64](FromSlice(s))
fmt.Printf("%T %v", c, c)
// Output:
// uint64 3
Distinct() returns distinct elements from a sequence. This function works only with sequences of comparable items.
s := []int{2, 2, 3, 1, 2, 3}
r := Distinct(FromSlice(s))
fmt.Println(r.AsSlice())
// Output:
// [2 3 1]
First() returns undefined value and false if a sequence is empty or the first item and true otherwise.
s1 := []int{1, 2, 3}
var s2 []int
fmt.Println(FromSlice(s1).First())
fmt.Println(FromSlice(s2).First())
// Output:
// 1 true
// 0 false
FirstOrDefault() returns default value of type T if a sequence is empty or the first item otherwise.
s1 := []int{1, 2, 3}
var s2 []int
fmt.Println(FromSlice(s1).FirstOrDefault())
fmt.Println(FromSlice(s2).FirstOrDefault())
// Output:
// 1
// 0
Flatten() combines a sequence of item sequences into flat sequence of items.
s1 := FromSlice([]int{1, 2, 3})
s2 := FromSlice([]int{4, 5, 6})
s3 := FromSlice([]int{7, 8, 9})
ss := FromSlice([]Seq[int]{s1, s2, s3})
fmt.Println(Flatten(ss).AsSlice())
// Output:
// [1 2 3 4 5 6 7 8 9]
FlatMap() maps each sequence item to a sequence and flattens results.
s := []int{1, 2, 3}
r := FlatMap(FromSlice(s), func(i int) Seq[int] {
return Repeat(i).Take(i)
})
fmt.Println(r.AsSlice())
// Output:
// [1 2 2 3 3 3]
Reduce() applies an accumulator function over a sequence given a specified initial value.
sum := func(a int, b int) int { return a + b }
s1 := []int{1, 2, 3, 4, 5}
var s2 []int
fmt.Println(FromSlice(s1).Reduce(0, sum))
fmt.Println(FromSlice(s2).Reduce(0, sum))
// Output:
// 15
// 0
Fold() applies an accumulator function over a sequence given a specified initial value. Accumulator and item types can be different.
sum := func(a int, b int) int { return a + b }
appendInt := func(s []int, i int) []int {
return append(s, i)
}
s := FromSlice([]int{1, 2, 3})
fmt.Println(Fold(s, 0, sum))
fmt.Println(Fold(s, []int{4, 5}, appendInt))
// Output:
// 6
// [4 5 1 2 3]
ForEach() performs an action for each item in a sequence.
s := []string{"Huey", "Dewey", "Louie"}
FromSlice(s).ForEach(func(name string) {
fmt.Println(name)
})
// Output:
// Huey
// Dewey
// Louie
ForEnumerated() is similar to ForEach but passes item index as first parameter of action function.
s := []string{"Huey", "Dewey", "Louie"}
FromSlice(s).ForEnumerated(func(i int, name string) {
fmt.Println(i, name)
})
// Output:
// 0 Huey
// 1 Dewey
// 2 Louie
GroupBy() returns a map grouping sequence items by key produced by the given function. Key type must be comparable.
isEven := func(i int) bool { return i%2 == 0 }
s := []int{1, 2, 3, 4, 5, 6}
g := GroupBy(FromSlice(s), isEven)
for k, v := range g {
fmt.Printf("%v: %v\n", k, v)
}
// Unordered output:
// false: [1 3 5]
// true: [2 4 6]
Inspect() allows to observe items of a sequence by performing an action for each item.
isEven := func(i int) bool { return i%2 == 0 }
FromSlice([]int{1, 2, 3}).
Inspect(func(i int) {
fmt.Printf("before: %v\n", i)
}).
Filter(isEven).
Inspect(func(i int) {
fmt.Printf("after: %v\n", i)
}).
Count()
// Output:
// before: 1
// before: 2
// after: 2
// before: 3
Partition() returns two slices. The first one contains items satisfying the given condition, the second one contains the rest of them.
s := FromSlice([]int{1, 2, 3, 4, 5, 6})
t, f := s.Partition(func(i int) bool { return i%2 == 0 })
fmt.Println(t)
fmt.Println(f)
// Output:
// [2 4 6]
// [1 3 5]
Sum() returns sum of elements in a sequence. This function works with sequences of numbers.
s1 := []int{1, 2, 3, 4}
var s2 []int
fmt.Println(Sum(FromSlice(s1)))
fmt.Println(Sum(FromSlice(s2)))
// Output:
// 10
// 0
Product() returns product of elements in a sequence. This function works with sequences of numbers.
s1 := []int{1, 2, 3, 4}
var s2 []int
fmt.Println(Product(FromSlice(s1)))
fmt.Println(Product(FromSlice(s2)))
// Output:
// 24
// 1
Average() returns average number of a sequence of numbers. This function works with sequences of integer and float numbers.
s1 := []int{1, 2, 3, 4, 5}
fmt.Println(Average(FromSlice(s1)))
// Output:
// 3
Max() returns the maximum element of a sequence and true if the sequence is not empty or undefined value and false otherwise. This function works with sequences of integer numbers, float numbers and strings.
s1 := []int{1, 2, 3}
s2 := []string{"Huey", "Dewey", "Louie"}
var s3 []int
fmt.Println(Max(FromSlice(s1)))
fmt.Println(Max(FromSlice(s2)))
fmt.Println(Max(FromSlice(s3)))
// Output:
// 3 true
// Louie true
// 0 false
Min() returns the minimum element of a sequence and true if the sequence is not empty or undefined value and false otherwise. This function works with sequences of integer numbers, float numbers and strings.
s1 := []int{1, 2, 3}
s2 := []string{"Huey", "Dewey", "Louie"}
var s3 []int
fmt.Println(Min(FromSlice(s1)))
fmt.Println(Min(FromSlice(s2)))
fmt.Println(Min(FromSlice(s3)))
// Output:
// 1 true
// Dewey true
// 0 false
AsChan() creates a channel and sends all sequence items into it in separate goroutine. After the sequence exhausted the channel is closed. You can specify the channel capacity with cap parameter.
ch := FromSlice([]int{1, 2, 3}).AsChan(0)
for i := range ch {
fmt.Println(i)
}
// Output:
// 1
// 2
// 3
ToChan() sends sequence items to an existing channel in current goroutine so this call is blocking. This function does not close the channel after the sequence is exhausted.
ch := make(chan int)
go func() {
FromSlice([]int{1, 2}).ToChan(ch)
FromSlice([]int{3, 4}).ToChan(ch)
close(ch)
}()
for i := range ch {
fmt.Println(i)
}
// Output:
// 1
// 2
// 3
// 4
ToSlice() appends items of a sequence to an existing slice given by reference.
s1 := []int{1, 2}
s2 := []int{3, 4, 5}
FromSlice(s2).ToSlice(&s1)
fmt.Println(s1)
// Output:
// [1 2 3 4 5]
FilterP() filters a sequence of items based on the given predicate.
This is parallel version of Filter() using specified number of goroutines to process items in parallel.
WhereP() is an alias for FilterP(). Use whatever name you like.
isEven := func(i int) bool {
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
return i%2 == 0
}
s := []int{1, 2, 3, 4, 5, 6}
FromSlice(s).FilterP(2, isEven).ForEach(func(i int) {
fmt.Println(i)
})
// Unordered output:
// 2
// 4
// 6
MapP() projects items of a sequence using the given function.
This is parallel version of Map() using specified number of goroutines to process items in parallel.
SelectP() is an alias for MapP(). Use whatever name you like.
s := FromSlice([]int{1, 2, 3, 4, 5, 6})
MapP(2, s, func(i int) int {
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
return i * i
}).ForEach(func(i int) {
fmt.Println(i)
})
// Unordered output:
// 1
// 4
// 9
// 16
// 25
// 36
FlatMapP() maps each sequence item to a sequence and flattens results.
This is parallel version of FlatMap() using specified number of goroutines to process items in parallel.
s := FromSlice([]int{1, 2, 3})
FlatMapP(2, s, func(i int) Seq[int] {
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
return Repeat(i).Take(i)
}).ForEach(func(i int) {
fmt.Println(i)
})
// Unordered output:
// 1
// 2
// 2
// 3
// 3
// 3
ReduceP() applies an accumulator function over a sequence given a specified initial value.
This is parallel version of Reduce() using specified number of goroutines to process items in parallel.
sum := func(a int, b int) int {
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
return a + b
}
s1 := []int{1, 2, 3, 4, 5}
var s2 []int
fmt.Println(FromSlice(s1).ReduceP(2, 0, sum))
fmt.Println(FromSlice(s2).ReduceP(2, 0, sum))
// Output:
// 15
// 0
ForEachP() performs an action for each item in a sequence.
This is parallel version of ForEach() using specified number of goroutines to process items in parallel.
s := []string{"Huey", "Dewey", "Louie"}
FromSlice(s).ForEachP(2, func(name string) {
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
fmt.Println(name)
})
// Unordered output:
// Huey
// Dewey
// Louie
ForEnumeratedP() is similar to ForEachP() but passes item index as first parameter of action function.
This is parallel version of ForEnumerated() using specified number of goroutines to process items in parallel.
s := []string{"Huey", "Dewey", "Louie"}
FromSlice(s).ForEnumeratedP(2, func(i int, name string) {
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
fmt.Println(i, name)
})
// Unordered output:
// 0 Huey
// 1 Dewey
// 2 Louie
AllP() determines whether all items of a sequence satisfy the given condition.
This is parallel version of All() using specified number of goroutines to process items in parallel.
isEven := func(i int) bool {
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
return i%2 == 0
}
s1 := []int{2, 4, 6, 8, 10, 12, 14, 16}
s2 := []int{2, 4, 6, 8, 11, 12, 14, 16}
fmt.Println(FromSlice(s1).AllP(2, isEven))
fmt.Println(FromSlice(s2).AllP(2, isEven))
// Output:
// true
// false
AnyP() determines whether any item of a sequence satisfies the given condition.
This is parallel version of Any() using specified number of goroutines to process items in parallel.
isEven := func(i int) bool {
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
return i%2 == 0
}
s1 := []int{1, 3, 5, 7, 9, 11, 13, 15}
s2 := []int{1, 2, 4, 7, 9, 11, 13, 16}
fmt.Println(FromSlice(s1).AnyP(2, isEven))
fmt.Println(FromSlice(s2).AnyP(2, isEven))
// Output:
// false
// true
PartitionP() returns two slices. The first one contains items satisfying the given condition, the second one contains the rest of them.
This is parallel version of Partition() using specified number of goroutines to process items in parallel.
s := FromSlice([]int{1, 2, 3, 4, 5, 6})
t, f := s.PartitionP(1, func(i int) bool {
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
return i%2 == 0
})
for _, i := range t {
fmt.Println("even:", i)
}
for _, i := range f {
fmt.Println("odd:", i)
}
// Unordered output:
// even: 2
// even: 4
// even: 6
// odd: 1
// odd: 3
// odd: 5
ZipP() combines items of two sequences pairwise using a projection function.
This is parallel version of Zip() using specified number of goroutines to process items in parallel.
s1 := FromSlice([]int{1, 2, 3})
s2 := FromSlice([]int{4, 5, 6, 7})
ZipP(2, s1, s2, func(u int, v int) [2]int {
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
return [2]int{u, v}
}).ForEach(func(s [2]int) {
fmt.Println(s)
})
// Unordered output:
// [1 4]
// [2 5]
// [3 6]
Zip3P() is same as ZipP() but combines three sequences.
This is parallel version of Zip3() using specified number of goroutines to process items in parallel.
s1 := FromSlice([]int{1, 2, 3})
s2 := FromSlice([]int{4, 5, 6})
s3 := FromSlice([]int{7, 8, 9, 10})
Zip3P(2, s1, s2, s3, func(u int, v int, w int) [3]int {
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
return [3]int{u, v, w}
}).ForEach(func(s [3]int) {
fmt.Println(s)
})
// Unordered output:
// [1 4 7]
// [2 5 8]
// [3 6 9]