-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
Proposal Details
I propose adding a new function to the slices
package that would return an iterator yielding variable-length subslices of a given slice based on a provided function. There are two possible implementations of this.
Version 1
The first is something I have personally used several times before, as well as being similar to a function in the Elixir standard library, looks like
func ChunkFunc[T any, C comparable, S ~[]T](s S, chunker func(T) C) iter.Seq[[]T]
This function returns an iterator that yields subslices of s
composed of consecutive runs of elements for which chunker
returns the same value. In other words,
ChunkFunc([]int{-1, -2, -3, 1, 2, -1, 3, 2, 1}, func(v int) bool { return v > 0 })
would yield []int{-1, -2, -3}
, []int{1, 2}
, []int{-1}
, and then []int{3, 2, 1}
. This is useful for a number of different things. For example, let's say that you have a slice of lines of output from something, some of which are to stdout and some to stderr. If you want to output those with a header to indicate which is which, being able to group the consecutive runs of lines that were to each is very useful, and a function like this can do it quite efficiently.
groups := slices.ChunkFunc(outputs, func(output Output) string { return output.Destination })
for group := range groups {
fmt.Printf("%v:\n", group[0].Destination) // Like Chunk(), never yields an empty slice.
for _, output := range group {
fmt.Println(output.Text)
}
}
Version 2
The other possible implementation is to simply make the function always return a bool
, and then define that each time it returns true
is the start of a new chunk.
While this is not a function that I've personally had a use for, I'm not really stuck on either specifically simply because I'm pretty sure that both can be implemented using the other. I think it's probably easier to implement the comparable
one using the bool
version, though, as it would simply be something like the following untested function:
func ChunkBy[T any, C comparable, S ~[]S](s S, chunker func(T) C) iter.Seq[[]T] {
return func(yield func([]T) bool) {
first := true
var prev C
chunks := ChunkFunc(s, func(v T) bool {
check := chunker(v)
if first {
prev = check
return true
}
start := check != prev
prev = check
return start
})
chunks(yield)
}
}
Version 3
Another alternative is to simply provide both as, say, ChunkFunc()
for the bool
version and ChunkBy()
for the comparable
one.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status