simpleflow

package module
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 3, 2023 License: MIT Imports: 2 Imported by: 0

README

Go Reference Github tag Go version Build Status GoReportCard gopherbadger-tag-do-not-edit

SimpleFlow

SimpleFlow is a a collection of generic functions and patterns that help building common workflows. Please see the tests for examples on how to use these functions.

Why should I use Simpleflow?

  • A single library for common workflows so you do not have to reinvent the wheel, maintain your own library or copy-paste code.
  • Simple and easy to use API.
  • Detailed documentation and examples.
  • Worker pools are simple, worker pools with error handling are not.
  • 100% test coverage

Installation

go get -u github.com/lobocv/simpleflow

Table of Contents

  1. Channels
  2. Work Pools
    1. Example
    2. Canceling a running worker pool
  3. Fan-Out and Fan-In
  4. Round Robin
  5. Batching
  6. Incremental Batching
  7. Transforming
  8. Filtering
  9. Extracting
  10. Segmenting
  11. Deduplication
  12. Counter
  13. Time
  14. Time Series

Channels

Some common but tedious operations on channels are done by the channel functions:

Example:

items := make(chan int, 3)
// push 1, 2, 3 onto the channel
LoadChannel(items, 1, 2, 3)
// Close the channel so ChannelToSlice doesn't block.
close(items) 
out := ChannelToSlice(items)
// out == []int{1, 2, 3}

Worker Pools

Worker pools provide a way to spin up a finite set of go routines to process items in a collection.

  • WorkerPoolFromSlice - Starts a fixed pool of workers that process elements in the slice
  • WorkerPoolFromMap - Starts a fixed pool of workers that process key-value pairs in the map
  • WorkerPoolFromChan - Starts a fixed pool of workers that process values read from a channel

These functions block until all workers finish processing.

WorkerPoolFromSlice example
ctx := context.Background()
items := []int{0, 1, 2, 3, 4, 5}
out := NewSyncMap(map[int]int{})
nWorkers := 2
f := func(_ context.Context, v int) error {
    out.Set(v, v*v)
    return nil
}
errors := WorkerPoolFromSlice(ctx, items, nWorkers, f)
// errors == []error{}
// out == map[int]int{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
Canceling a running worker pool
// Create a cancel-able context
ctx, cancel := context.WithCancel(context.Background())

items := []int{0, 1, 2, 3, 4, 5}
out := NewSyncMap(map[int]int{}) // threadsafe map used in tests
nWorkers := 2

f := func(_ context.Context, v int) error {
    // Cancel as soon as we hit v > 2
    if v > 2 {
        cancel()
        return nil
    }
    out.Set(v, v*v)
    return nil
}
WorkerPoolFromSlice(ctx, items, nWorkers, f)
// errors == []error{}
// out == map[int]int{0: 0, 1: 1, 2: 4}

Fan-Out and Fan-In

FanOut and FanIn provide means of fanning-in and fanning-out channel to other channels.

Example:

// Generate some data on a channel (source for fan out)
N := 3
source := make(chan int, N)
data := []int{1, 2, 3}
for _, v := range data {
    source <- v
}
close(source)

// Fan out to two channels. Each will get a copy of the data
fanoutSink1 := make(chan int, N)
fanoutSink2 := make(chan int, N)
FanOutAndClose(source, fanoutSink1, fanoutSink2)

// Fan them back in to a single channel. We should get the original source data with two copies of each item
fanInSink := make(chan int, 2*N)
FanInAndClose(fanInSink, fanoutSink1, fanoutSink2)
fanInResults := ChannelToSlice(fanInSink)
// fanInResults == []int{1, 2, 3, 1, 2, 3}

Round Robin

RoundRobin distributes values from a channel over other channels in a round-robin fashion

Example:

// Generate some data on a channel
N := 5
source := make(chan int, N)
data := []int{1, 2, 3, 4, 5}
for _, v := range data {
    source <- v
}
close(source)

// Round robin the data into two channels, each should have half the data
sink1 := make(chan int, N)
sink2 := make(chan int, N)
RoundRobin(source, fanoutSink1, sink2)
CloseManyWriters(fanoutSink1, sink2)

sink1Data := ChannelToSlice(sink1)
// sink1Data == []int{1, 3, 5}
sink2Data := ChannelToSlice(sink2)
// sink2Data == []int{2, 4}

Batching

BatchMap, BatchSlice and BatchChan provide ways to break maps, slices and channels into smaller components of at most N size.

Example:

items := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
batches := BatchSlice(items, 2)
// batches == [][]int{{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}}
items := map[int]int{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5}
batches := BatchMap(items, 3)
// batches == []map[int]int{ {0: 0, 3: 3, 4: 4}, {1: 1, 2: 2, 5: 5} }

Incremental Batching

Batching can also be done incrementally by using IncrementalBatchSlice and IncrementalBatchMap functions. These functions are meant to be called repeatedly, adding elements until a full batch can be processed, at which time, the batch is returned.

Example:

batchSize := 3
var items, batch []int
items, batch = IncrementalBatchSlice(items, batchSize, 1)
// items == []int{1}, batch == nil

items, batch = IncrementalBatchSlice(items, batchSize, 2)
// items == []int{1, 2}, batch == nil

items, batch = IncrementalBatchSlice(items, batchSize, 3)
// Batch size reached
// items == []int{}, batch == []int{1, 2, 3}

items, batch = IncrementalBatchSlice(items, batchSize, 4)
// items == []int{4}, batch == nil

Transforming

Transformation operations (often named map() in other languages) allow you to transform each element of a slice to another value using a given transformation function.

out := Transform([]int{1, 2, 3}, func(t int) string {
    return strconv.Itoa(t)
})
// out == []string{"1", "2", "3"}

out = TransformAndFilter([]int{1, 2, 3, 4, 5}, func(t int) (int, bool) {
    return 2 * t,  t%2 == 0
})
// out == []string{4, 8}

Filtering

Filtering operations allows you to remove elements from slices or maps. Filtering can done either in-place with FilterSliceInplace, FilterMapInPlace or by creating a copy FilterSlice, FilterMap. The filtering function argument must return true if you want to keep the element.

The following example filters the positive numbers from the slice of integers:

getPositive := func(t int) bool {
    return t > 0
}

out := FilterSlice([]int{5, -2, 3, 1, 0, -3, -5, -6}, getPositive)
// out == []int{5, 3, 1}

Extracting

Extraction operations allow you to extract one or more elements from a slice. Extraction functions accept a function which takes in each element of the slice and returns the element to extract (potentially different than the input type) and a boolean for whether the element should be extracted.

The following example extracts the Name field from a slice of Object.

type Object struct {
    Name string
}

in := []Object{
    {Name: "John"},
    {Name: "Paul"},
    {Name: "George"},
    {Name: "Ringo"},
    {Name: "Bob"},
}
var names []string

fn := func(t Object) (string, bool) {
    if t.Name == "Bob" {
        return "", false
    }
    return t.Name, true
}

names = ExtractToSlice(in, fn, names)
// names == []string{"John", "Paul", "George", "Ringo"}

ExtractFirst() can be used to extract the first element in the slice. The following example extracts the first value larger than 4.

values := []int{4, 1, 5, 7}
fn := func(v int) bool {
    return v > 4
}

v, found := ExtractFirst(values, fn)
// v == 5, found == true

Segmenting

SegmentSlice, SegmentMap and SegmentChan allow you to split a slice or map into sub-slices or maps based on the provided segmentation function:

Segmenting a slice into even and odd values
items := []int{0, 1, 2, 3, 4, 5}

segments := SegmentSlice(items, func(v int) int {
    if v % 2 == 0 {
        return "even"
	}
        return "odd"
})
// segments == map[string][]int{"even": {0, 2, 4}, "odd": {1, 3, 5}}

Deduplication

A series of values can be deduplicated using the Deduplicator{}. It can either accept the entire slice:

deduped := Deduplicate([]int{1, 1, 2, 2, 3, 3})
// deduped == []int{1, 2, 3}

or iteratively deduplicate for situations where you want fine control with a for loop.

dd := NewDeduplicator[int]()
values := []int{1, 1, 2, 3, 3}
deduped := []int{}

for _, v := range values {
    seen := dd.Seen(v) 
    // seen == true for index 1 and 4
    isNew := dd.Add(v) 
    // isNew == true for index 0, 2 and 3
    if isNew {
        deduped = append(deduped, v)	
    }
}

Complex objects can also be deduplicated using the ObjectDeduplicator{}, which requires providing a function that creates unique IDs for the provided objects being deduplicated. This is useful for situations where the values being deduplicated are not comparable (ie, have a slice field) or if you want more fine control over just what constitutes a duplicate.

// Object is a complex structure that cannot be used with a regular Deduplicator as it contains 
// a slice field, and thus is not `comparable`.
type Object struct {
    slice   []int
    pointer *int
    value   string
}

// Create a deduplicator that deduplicates Object's by their "value" field.
dd := NewObjectDeduplicator[Object](func(v Object) string {
        return v.value
    })

Counter

The Counter{} and ObjectCounter{} can be used to count the number of occurrences of values. Much like the Deduplicator{}, the Counter{} works well for simple types.

counter := NewCounter[int]()
values := []int{1, 1, 2, 3, 3, 3, 3}

// Add the values to the counter, values can also be added individually with counter.Add()
currentCount := counter.AddMany(values) 

numberOfOnes := counter.Count(1) // returns 2
numberOfTwos := counter.Count(2) // returns 1
numberOfThrees := counter.Count(3) // returns 4

Complex objects can also be counted using the ObjectCounter{}, which requires providing a function that creates buckets for the provided objects being deduplicated. This is useful for situations where the values being counted are not comparable (ie, have a slice field) or if you want more fine control over the bucketing logic (ie bucket objects by a certain field value).

// Object is a complex structure that cannot be used with a regular Counter as it contains 
// a slice field, and thus is not `comparable`.
type Object struct {
    slice   []int
    pointer *int
    value   string
}

// Create a counter that counts Object's bucketed by their "value" field.
counter := NewObjectCounter[Object](func(v Object) string {
        return v.value
    })

Time

The simeplflow/time package provides functions that assist with working with the standard library time package and time.Time objects. The package contains functions to define, compare and iterate time ranges.

Timeseries

The simpleflow/timeseries packages contains a generic TimeSeries object that allows you to manipulate timestamped data. TimeSeries store unordered time series data in an underlying map[time.Time]V. Each TimeSeries is configured with a TimeTransformation which applies to each time.Time key when accessed. This makes storing time series data with a particular time granularity easy. For example, with a TimeTransformation that truncates to the day, any time.Time object in the given day will access the same key.

Example:

// TF is a TimeTransformation that truncates the time to the start of the day
func TF(t time.Time) time.Time {
    return t.UTC().Truncate(24 * time.Hour)
}

// Day is a function to create a Time object on a given day offset from Jan 1st 2022 by the `i`th day
func Day(i int) time.Time {
    return time.Date(2022, 01, i, 0, 0, 0, 0, time.UTC)
}

func main() {
    data := map[time.Time]int{
            Day(0): 0, // Jan 1st 2022
            Day(1): 1, // Jan 2nd 2022
            Day(2): 2, // Jan 3rd 2022
        }
    ts := timeseries.NewTimeSeries(data, TF)
	
	// Get the value on Jan 2th at 4am and at 5 am
	// The values for `a` and `b` are both == 1 because the hour is irrelevant
	// when accessing data using the TF() time transform
	a := ts.Get(time.Date(2022, 01, 2, 4, 0, 0, 0, time.UTC))
	b := ts.Get(time.Date(2022, 01, 2, 5, 0, 0, 0, time.UTC))
	// a == b == 1 
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BatchChan

func BatchChan[T any](items <-chan T, size int, to chan []T)

BatchChan reads from a channel and pushes batches of size `size` onto the `to` channel

func BatchMap

func BatchMap[K comparable, V any](items map[K]V, size int) []map[K]V

BatchMap takes a map and breaks it up into sub-maps of `size` keys each

func BatchSlice

func BatchSlice[T any](items []T, size int) [][]T

BatchSlice takes a slice and breaks it up into sub-slices of `size` length each

func ChannelIntoSlice

func ChannelIntoSlice[T any](ch chan T, out []T) []T

ChannelIntoSlice reads elements from the channel and returns appends them to the `out` slice. This operation will block until the channel is closed

func ChannelToSlice

func ChannelToSlice[T any](ch chan T) (out []T)

ChannelToSlice reads elements from the channel and returns them as a slice. This operation will block until the channel is closed

func CloseMany

func CloseMany[T any](channels ...chan T)

CloseMany closes all of the given channels

func CloseManyWriters

func CloseManyWriters[T any](channels ...chan<- T)

CloseManyWriters closes all of the given write-only channels

func Deduplicate added in v0.1.6

func Deduplicate[V comparable](values []V) []V

Deduplicate returns a newly allocated slice without deplicate values

func ExtractFirst added in v1.2.0

func ExtractFirst[T any](values []T, fn func(T) bool) (v T, exists bool)

ExtractFirst returns the first element in the slice for which fn(element) == true. If no matches are found, the second return argument is false.

func ExtractToChannel added in v1.2.0

func ExtractToChannel[T, V any](in []T, fn func(T) (V, bool), out chan V)

ExtractToChannel calls the func `fn` for each element in `in` and pushes the result to `out` only if the second return argument of `fn` is true.

func ExtractToSlice added in v1.2.0

func ExtractToSlice[T, V any](in []T, fn func(T) (V, bool), out []V) []V

ExtractToSlice calls the func `fn` for each element in `in` and appends the result to `out` only if the second return argument of `fn` is true.

func FanIn

func FanIn[T any](to chan<- T, from ...<-chan T)

FanIn reads from each `from` channel and writes to the `to` channel

func FanInAndClose

func FanInAndClose[T any](to chan<- T, from ...<-chan T)

FanInAndClose reads from each `from` channel and writes to the `to` channel It closes the `to` channel once all messages are drained from the `from` channels.

func FanOut

func FanOut[T any](from <-chan T, to ...chan<- T)

FanOut reads from the `from` channel and publishes the data across all `to` channels

func FanOutAndClose

func FanOutAndClose[T any](from <-chan T, to ...chan<- T)

FanOutAndClose reads from the `from` channel and publishes the data across all `to` channels It closes each `to` channel once all messages are drained from the `from` channels.

func FilterMap added in v1.2.0

func FilterMap[K comparable, V any](in map[K]V, fn func(k K, v V) bool) map[K]V

FilterMap filters the input map with the function `fn` and return a new map The function `fn` accepts a key, value pair and should return true to keep the pair in the map

func FilterMapInplace added in v1.2.0

func FilterMapInplace[K comparable, V any](in map[K]V, fn func(k K, v V) bool)

FilterMapInplace filters the input map with the function `fn` in-place The function `fn` accepts a key, value pair and should return true to keep the pair in the map

func FilterSlice added in v1.2.0

func FilterSlice[V any](in []V, fn func(v V) bool) []V

FilterSlice filters the input slice with the function `fn` and return a new slice The function `fn` accepts a value and should return true to copy the value into the new slice

func FilterSliceInplace added in v1.2.0

func FilterSliceInplace[V any](in []V, fn func(v V) bool) []V

FilterSliceInplace filters the input slice with the function `fn` in-place The function `fn` accepts a value and should return true to keep the value in the slice

func FilterSliceInto added in v1.2.0

func FilterSliceInto[V any](in, out []V, fn func(v V) bool) []V

FilterSliceInto filters the input slice `in` with the function `fn` into `out` The function `fn` accepts a value and should return true to copy the value into the `out` slice

func IncrementalBatchMap

func IncrementalBatchMap[K comparable, V any](items map[K]V, batchSize int, k K, v V) (batch map[K]V)

IncrementalBatchMap incrementally builds map batches of size `batchSize` by adding elements to a map If the map is larger than `batchSize` elements, a single batch is returned along with the remaining elements of the map. Batched items are chosen by iterating the (unordered) map and thus you cannot make assumptions on which keys will exist in the batch. To avoid errors on the caller side, passing a batchSize < 1 will result in a batchSize of 1.

func IncrementalBatchSlice

func IncrementalBatchSlice[T any](items []T, batchSize int, v T) (remaining, batch []T)

IncrementalBatchSlice incrementally builds slice batches of size `batchSize` by appending to a slice If the slice is larger than `batchSize` elements, a single batch is returned. The remaining elements of the slice are always returned. Batched items are returned from the head of the slice. To avoid errors on the caller side, passing a batchSize < 1 will result in a batchSize of 1.

func IncrementalSegmentMap added in v1.1.0

func IncrementalSegmentMap[K comparable, V any, S comparable](segments map[S]map[K]V, k K, v V, f SegmentFuncKV[K, V, S])

IncrementalSegmentMap adds the (key,value) pair to the correct segment inside `segments by calling `f` on `(k, v)`

func IncrementalSegmentSlice added in v1.1.0

func IncrementalSegmentSlice[T any, S comparable](segments map[S][]T, item T, f SegmentFunc[T, S])

IncrementalSegmentSlice adds the item to the correct segment inside `segments by calling `f` on `item`

func LoadChannel

func LoadChannel[T any](ch chan<- T, items ...T)

LoadChannel puts all elements from `items` onto the channel `ch` This operation will block if not all items fit within the channel buffer or if there is not simultaneously another go routine reading from the channel.

func RoundRobin

func RoundRobin[T any](from <-chan T, to ...chan<- T)

RoundRobin reads from the `from` channel and distributes the values in a round-robin fashion to the `to` channels.

func SegmentChan

func SegmentChan[T any, S comparable](items <-chan T, f SegmentFunc[T, S]) map[S][]T

SegmentChan takes a channel and breaks it into smaller segmented slices using the provided function `f` The segments are returned in a map where the segment is the key.

func SegmentMap

func SegmentMap[K comparable, V any, S comparable](items map[K]V, f SegmentFuncKV[K, V, S]) map[S]map[K]V

SegmentMap takes a map and breaks it into smaller segmented maps using the provided function `f` The segments are returned in a map where the segment is the key.

func SegmentSlice

func SegmentSlice[T any, S comparable](items []T, f SegmentFunc[T, S]) map[S][]T

SegmentSlice takes a slice and breaks it into smaller segmented slices using the provided function `f` The segments are returned in a map where the segment is the key.

func Transform added in v1.2.0

func Transform[T, V any](values []T, fn func(t T) V) []V

Transform applies a transformation function to each element in the input slice and returns a new slice

func TransformAndFilter added in v1.2.0

func TransformAndFilter[T, V any](values []T, fn func(t T) (V, bool)) []V

TransformAndFilter applies a transformation function to each element in the input slice. If the second return value of the transformation function is false, then the value will be omitted from the output.

func WorkerPoolFromChan

func WorkerPoolFromChan[T any](ctx context.Context, items <-chan T, nWorkers int, f Job[T]) []error

WorkerPoolFromChan starts a worker pool of size `nWorkers` and calls the function `f` for each element in the `items` channel

func WorkerPoolFromMap

func WorkerPoolFromMap[K comparable, V any](ctx context.Context, items map[K]V, nWorkers int, f JobKV[K, V]) []error

WorkerPoolFromMap starts a worker pool of size `nWorkers` and calls the function `f` for each element in the `items` map

func WorkerPoolFromSlice

func WorkerPoolFromSlice[T any](ctx context.Context, items []T, nWorkers int, f Job[T]) []error

WorkerPoolFromSlice starts a worker pool of size `nWorkers` and calls the function `f` for each element in the `items` slice. It returns an array of errors from the jobs.

Types

type Counter added in v0.1.7

type Counter[T comparable] struct {
	// contains filtered or unexported fields
}

Counter is an entity that keeps track of the number items it encounters

func NewCounter added in v0.1.7

func NewCounter[T comparable]() *Counter[T]

NewCounter returns a new Counter which can be used to deduplicate slices values

func (*Counter[T]) Add added in v0.1.7

func (c *Counter[T]) Add(v T) int

Add adds a item to the Counter and returns the current number of occurrences

func (*Counter[T]) AddMany added in v0.1.7

func (c *Counter[T]) AddMany(values []T)

AddMany adds all the values in the provided slice to the counter

func (*Counter[T]) Count added in v0.1.7

func (c *Counter[T]) Count(v T) int

Count returns the current number of occurrences for the given value

func (*Counter[T]) Reset added in v0.1.7

func (c *Counter[T]) Reset()

Reset clears the values in the Counter{}

type Deduplicator added in v0.1.6

type Deduplicator[T comparable] struct {
	// contains filtered or unexported fields
}

Deduplicator is an entity that keeps track of items it has seen before so that it can deduplicate values

func NewDeduplicator added in v0.1.6

func NewDeduplicator[T comparable]() *Deduplicator[T]

NewDeduplicator returns a new Deduplicator which can be used to deduplicate slices values

func (*Deduplicator[T]) Add added in v0.1.6

func (dd *Deduplicator[T]) Add(v T) bool

Add adds a item to the Deduplicator and returns true if it was a new value (ie not a duplicate)

func (*Deduplicator[T]) Deduplicate added in v0.1.6

func (dd *Deduplicator[T]) Deduplicate(values []T) []T

Deduplicate returns a newly allocated slice without duplicate values by comparing it against values previously seen by the Deduplicator{}

func (*Deduplicator[T]) DeduplicateIndices added in v0.1.6

func (dd *Deduplicator[T]) DeduplicateIndices(values []T) []int

DeduplicateIndices returns the indices of values in the provided slice which are duplicates

func (*Deduplicator[T]) Reset added in v0.1.6

func (dd *Deduplicator[T]) Reset()

Reset removes any memory of duplicate values seen by this Deduplicator{}

func (*Deduplicator[T]) Seen added in v0.1.6

func (dd *Deduplicator[T]) Seen(v T) bool

Seen returns true if the provided value has already been added to the Deduplicator

type Job

type Job[T any] func(ctx context.Context, item T) error

Job is a function that the slice or channel worker pool executes

type JobKV

type JobKV[K comparable, V any] func(context.Context, K, V) error

JobKV is a function that the map worker pool executes

type KeyValue

type KeyValue[K comparable, V any] struct {
	Key   K
	Value V
}

KeyValue is a tuple of key, value

type ObjectCounter added in v0.1.7

type ObjectCounter[T any] struct {
	// contains filtered or unexported fields
}

ObjectCounter is a counter that works on objects by creating an ID for each element. Objects with the same ID will be counted in the same bucket.

func NewObjectCounter added in v0.1.7

func NewObjectCounter[T any](toId func(T) string) *ObjectCounter[T]

NewObjectCounter creates a ObjectCounter that uses the provided function in order to create IDs for needing to be counted.

func (*ObjectCounter[T]) Add added in v0.1.7

func (c *ObjectCounter[T]) Add(v T) int

Add adds an object to the Counter and returns the current number of occurrences

func (*ObjectCounter[T]) AddMany added in v0.1.7

func (c *ObjectCounter[T]) AddMany(values []T)

AddMany adds all the values in the provided slice to the counter

func (*ObjectCounter[T]) Count added in v0.1.7

func (c *ObjectCounter[T]) Count(v T) int

Count returns the current number of occurrences for the given object

func (*ObjectCounter[T]) Reset added in v0.1.7

func (c *ObjectCounter[T]) Reset()

Reset clears the values in the ObjectCounter{}

type ObjectDeduplicator added in v0.1.6

type ObjectDeduplicator[T any] struct {
	// contains filtered or unexported fields
}

ObjectDeduplicator is a deduplicator that works on objects by creating an ID for each element. Objects with the same ID will be deduplicated.

func NewObjectDeduplicator added in v0.1.6

func NewObjectDeduplicator[T any](toId func(T) string) *ObjectDeduplicator[T]

NewObjectDeduplicator creates a ObjectDeduplicator that uses the provided function in order to create IDs for needing to be deduplicated.

func (*ObjectDeduplicator[T]) Add added in v0.1.6

func (dd *ObjectDeduplicator[T]) Add(v T) bool

Add adds a item to the ObjectDeduplicator and returns true if it was a new value (ie not a duplicate)

func (*ObjectDeduplicator[T]) Deduplicate added in v0.1.6

func (dd *ObjectDeduplicator[T]) Deduplicate(values []T) []T

Deduplicate returns a newly allocated slice without duplicate values by comparing it against values previously seen by the ObjectDuplicator{}

func (*ObjectDeduplicator[T]) DeduplicateIndices added in v0.1.6

func (dd *ObjectDeduplicator[T]) DeduplicateIndices(values []T) []int

DeduplicateIndices returns the indices of values in the provided slice which are duplicates

func (*ObjectDeduplicator[T]) Reset added in v0.1.6

func (dd *ObjectDeduplicator[T]) Reset()

Reset removes any memory of duplicate values seen by this Deduplicator{}

func (*ObjectDeduplicator[T]) Seen added in v0.1.6

func (dd *ObjectDeduplicator[T]) Seen(v T) bool

Seen returns true if the provided value has already been added to the ObjectDeduplicator

type SegmentFunc

type SegmentFunc[T any, S comparable] func(T) S

SegmentFunc is a function that determines how an item of type `T` is segmented into a segment of type `S`

type SegmentFuncKV

type SegmentFuncKV[K comparable, V any, S comparable] func(K, V) S

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL