collection

package module
v2.0.0 Latest Latest
Warning

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

Go to latest
Published: Jul 3, 2025 License: MIT Imports: 9 Imported by: 5

README

collection

PkgGoDev Build and Test

Usage

Available Data Structures:

Dictionary

This is a logical set of strings. It utilizes a prefix tree model to be very space efficient.

LinkedList

A collection that offers fast, consistent insertion time when adding to either the beginning or end. Accessing a random element is slower than other similar list data structures.

List

Similar to a C++ Vector, Java ArrayList, or C# List this is a wrapper over top of arrays that allows for quick random access, but somewhat slower insertion characteristics than a LinkedList.

LRUCache

This name is short for "Least Recently Used Cache". It holds a predetermined number of items, and as new items inserted, the least recently added or read item will be removed. This can be a useful way to build a tool that uses the proxy pattern to have quick access to the most useful items, and slower access to any other item. There is a memory cost for this, but it's often worth it.

Queue

Stores items without promising random access. The first thing you put in will be the first thing you get out.

Stack

Stores items without promising random access. The first thing you put in will be the last thing you get out.

Querying Collections

Inspired by .NET's Linq, querying data structures used in this library is a snap! Build Go pipelines quickly and easily which will apply lambdas as they query your data structures.

Slices

Converting between slices and a queryable structure is as trivial as it should be.

original := []string{"a", "b", "c"}
subject := collection.AsEnumerable(original...)

for entry := range subject.Enumerate(context.Background()) {
    fmt.Println(entry)
}
// Output:
// a
// b
// c

Where
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

subject := collection.AsEnumerable[int](1, 2, 3, 4, 5, 6)
filtered := collection.Where(subject, func(num int) bool{
    return num > 3
})
for entry := range filtered.Enumerate(ctx) {
    fmt.Println(entry)
}
// Output:
// 4
// 5
// 6
Select
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

subject := collection.AsEnumerable[int](1, 2, 3, 4, 5, 6)
updated := collection.Select[int](subject, func(num int) int {
    return num + 10
})
for entry := range updated.Enumerate(ctx) {
    fmt.Println(entry)
}

// Output:
// 11
// 12
// 13
// 14
// 15
// 16

Queues

Creating a Queue
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

subject := collection.NewQueue(1, 2, 3, 5, 8, 13, 21)
selected := subject.Enumerate(ctx).Skip(3).Take(3)
for entry := range selected {
	fmt.Println(entry)
}

// Output:
// 5
// 8
// 13
Checking if a Queue is empty
populated := collection.NewQueue(1, 2, 3, 5, 8, 13)
notPopulated := collection.NewQueue[int]()
fmt.Println(populated.IsEmpty())
fmt.Println(notPopulated.IsEmpty())
// Output:
// false
// true

Other utilities

Fibonacci

This was added to test Enumerable types that have no logical conclusion. But it may prove useful other places, so it is available in the user-facing package and not hidden away in a test package.

Filesystem

Find the standard library's pattern for looking through a directory cumbersome? Use the collection querying mechanisms seen above to search a directory as a collection of files and child directories.

Versioning

This library will conform to strict semantic versions as defined by semver.org's v2 specification.

Contributing

I accept contributions! Please submit PRs to the main or v1 branches. Remember to add tests!

F.A.Q.

Should I use v1 or v2?

If you are newly adopting this library, and are able to use Go 1.18 or newer, it is highly recommended that you use v2.

V2 was primarily added to support Go generics when they were introduced in Go 1.18, but there were other breaking changes made because of the opportunity to do with the major version bump.

Because it's not reasonable to expect everybody to adopt the newest versions of Go immediately as they're released, v1 of this library wil be activey supported until Go 1.17 is no longer supported by the Go team. After that community contributions to v1 will be entertained, but active development won't be ported to the v1 branch.

Why does Enumerate take a context.Context?

Having a context associated with the enumeration allows for cancellation. This is valuable in some scenarios, where enumeration may be a time-consuming operation. For example, imagine an Enumerable that wraps a web API which returns results in pages. Injecting a context allows for you to add operation timeouts, and otherwise protect yourself from an operation that may not finish quickly enough for you (or at all.)

However, under the covers an Enumerator[T] is a <-chan T. This decision means that a separate goroutine is used to publish to the channel while your goroutine reads from it.

That means if your code stops before all items in the Enumerator are read, a goroutine and all of the memory it's using will be leaked.

This is a known problem, and it's understood why it's not ideal. The workaround is easy - if there's ever a chance you won't enumerate all items, protect yourself by using the following pattern:

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// ...

for item := range myEnumerable.Enumerate(ctx) {
    // ...
}

Documentation

Overview

Package collection seeks to provide an expressive and readable way of working with basic data structures in Go.

As a former .NET developer, I deeply missed writing programs in the style of Linq. Doing so enables concurrent/ parallel reactive programs to be written in a snap. Go's functional nature enables us to have a very similar, if more verbose, experience.

Take for example the scenario of printing the number of Go source files in a directory. Using this package, this takes only a few lines:

myDir := collection.Directory{
	Location: "./",
}

results := myDir.Enumerate(context.Background()).Where(func(x interface{}) bool {
	return strings.HasSuffix(x.(string), ".go")
})

fmt.Println(results.CountAll())

A directory is a collection of filesystem entries, so we're able to iterate through them using the "Enumerate" function. From there, we filter on only file names that end with ".go". Finally, we print the number of entries that were encountered.

This is a trivial example, but imagine building more elaborate pipelines. Maybe take advantage of the `SelectParallel` function which allows multiple goroutines to process a single transform at once, with their results being funnelled into the next phase of the pipeline. Suddenly, injecting new steps can be transparent.

Index

Examples

Constants

View Source
const (
	DirectoryOptionsExcludeFiles = 1 << iota
	DirectoryOptionsExcludeDirectories
	DirectoryOptionsRecursive
)

These constants define all of the supported options for configuring a `Directory`

Variables

View Source
var (
	ErrUnexpectedType = errors.New("value was of an unexpected type")
)

A collection of errors that may be thrown by functions in this file.

Functions

func All

func All[T any](subject Enumerable[T], p Predicate[T]) bool

All tests whether or not all items present in an Enumerable meet a criteria.

func Any

func Any[T any](iterator Enumerable[T]) bool

Any tests an Enumerable to see if there are any elements present.

func Anyp

func Anyp[T any](iterator Enumerable[T], p Predicate[T]) bool

Anyp tests an Enumerable to see if there are any elements present that meet a criteria.

func Count

func Count[T any](iter Enumerable[T], p Predicate[T]) int

Count iterates over a list and keeps a running tally of the number of elements which satisfy a predicate.

func CountAll

func CountAll[T any](iter Enumerable[T]) int

CountAll iterates over a list and keeps a running tally of how many it's seen.

func ElementAt

func ElementAt[T any](iter Enumerable[T], n uint) T

ElementAt retreives an item at a particular position in an Enumerator.

func First

func First[T any](subject Enumerable[T]) (retval T, err error)

First retrieves just the first item in the list, or returns an error if there are no elements in the array.

Example
empty := collection.NewQueue[int]()
notEmpty := collection.NewQueue(1, 2, 3, 4)

fmt.Println(collection.First[int](empty))
fmt.Println(collection.First[int](notEmpty))
Output:

0 enumerator encountered no elements
1 <nil>

func IsErrorMultipleElements

func IsErrorMultipleElements(err error) bool

IsErrorMultipleElements determines whether or not the given error is the result of multiple values being returned when one or zero were expected.

func IsErrorNoElements

func IsErrorNoElements(err error) bool

IsErrorNoElements determines whethr or not the given error is the result of no values being returned when one or more were expected.

func Last

func Last[T any](iter Enumerable[T]) T

Last retreives the item logically behind all other elements in the list.

Example
subject := collection.NewList(1, 2, 3, 4)
fmt.Println(collection.Last[int](subject))
Output:

4

func Single

func Single[T any](iter Enumerable[T]) (retval T, err error)

Single retreives the only element from a list, or returns nil and an error.

func Singlep

func Singlep[T any](iter Enumerable[T], pred Predicate[T]) (retval T, err error)

Singlep retrieces the only element from a list that matches a criteria. If no match is found, or two or more are found, `Singlep` returns nil and an error.

func ToSlice

func ToSlice[T any](iter Enumerable[T]) []T

ToSlice places all iterated over values in a Slice for easy consumption.

func UCount

func UCount[T any](iter Enumerable[T], p Predicate[T]) uint

UCount iterates over a list and keeps a running tally of the number of elements satisfy a predicate.

Example
subject := collection.NewStack[any](9, 'a', "str1")
result := collection.UCount[interface{}](subject, func(a interface{}) bool {
	_, ok := a.(string)
	return ok
})
fmt.Println(result)
Output:

1

func UCountAll

func UCountAll[T any](iter Enumerable[T]) uint

UCountAll iterates over a list and keeps a running tally of how many it's seen.

Example
subject := collection.NewStack(8, 9, 10, 11)
fmt.Println(collection.UCountAll[int](subject))
Output:

4

Types

type Comparator

type Comparator[T any] func(a, b T) (int, error)

Comparator is a function which evaluates two values to determine their relation to one another. - Zero is returned when `a` and `b` are equal. - Positive numbers are returned when `a` is greater than `b`. - Negative numbers are returned when `a` is less than `b`.

type Dictionary

type Dictionary struct {
	// contains filtered or unexported fields
}

Dictionary is a list of words. It is implemented as a Trie for memory efficiency.

func (*Dictionary) Add

func (dict *Dictionary) Add(word string) (wasAdded bool)

Add inserts a word into the dictionary, and returns whether or not that word was a new word.

Time complexity: O(m) where 'm' is the length of word.

Example
subject := &collection.Dictionary{}

const example = "hello"
fmt.Println(subject.Contains(example))
fmt.Println(subject.Size())
subject.Add(example)
fmt.Println(subject.Contains(example))
fmt.Println(subject.Size())
Output:

false
0
true
1

func (*Dictionary) Clear

func (dict *Dictionary) Clear()

Clear removes all items from the dictionary.

Example
subject := &collection.Dictionary{}

subject.Add("hello")
subject.Add("world")

fmt.Println(subject.Size())
fmt.Println(collection.CountAll[string](subject))

subject.Clear()

fmt.Println(subject.Size())
fmt.Println(collection.Any[string](subject))
Output:

2
2
0
false

func (Dictionary) Contains

func (dict Dictionary) Contains(word string) bool

Contains searches the Dictionary to see if the specified word is present.

Time complexity: O(m) where 'm' is the length of word.

func (Dictionary) Enumerate

func (dict Dictionary) Enumerate(ctx context.Context) Enumerator[string]

Enumerate lists each word in the Dictionary alphabetically.

Example
subject := &collection.Dictionary{}
subject.Add("world")
subject.Add("hello")

upperCase := collection.Select[string](subject, strings.ToUpper)

for word := range subject.Enumerate(context.Background()) {
	fmt.Println(word)
}

for word := range upperCase.Enumerate(context.Background()) {
	fmt.Println(word)
}
Output:

hello
world
HELLO
WORLD

func (*Dictionary) Remove

func (dict *Dictionary) Remove(word string) (wasRemoved bool)

Remove ensures that `word` is not in the Dictionary. Returns whether or not an item was removed.

Time complexity: O(m) where 'm' is the length of word.

Example
const world = "world"
subject := &collection.Dictionary{}
subject.Add("hello")
subject.Add(world)

fmt.Println(subject.Size())
fmt.Println(collection.CountAll[string](subject))

subject.Remove(world)

fmt.Println(subject.Size())
fmt.Println(collection.CountAll[string](subject))
fmt.Println(collection.Any[string](subject))
Output:

2
2
1
1
true

func (Dictionary) Size

func (dict Dictionary) Size() int64

Size reports the number of words there are in the Dictionary.

Time complexity: O(1)

type Directory

type Directory struct {
	Location string
	Options  DirectoryOptions
}

Directory treats a filesystem path as a collection of filesystem entries, specifically a collection of directories and files.

func (Directory) Enumerate

func (d Directory) Enumerate(ctx context.Context) Enumerator[string]

Enumerate lists the items in a `Directory`

Example
traverser := collection.Directory{
	Location: ".",
	Options:  collection.DirectoryOptionsExcludeDirectories,
}

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

fileNames := collection.Select[string](traverser, path.Base)

filesOfInterest := collection.Where(fileNames, func(subject string) bool {
	return subject == "filesystem_examples_test.go"
})

for entry := range filesOfInterest.Enumerate(ctx) {
	fmt.Println(entry)
}
Output:

filesystem_examples_test.go

type DirectoryOptions

type DirectoryOptions uint

DirectoryOptions is a means of configuring a `Directory` instance to including various children in its enumeration without supplying a `Where` clause later.

type Enumerable

type Enumerable[T any] interface {
	Enumerate(ctx context.Context) Enumerator[T]
}

Enumerable offers a means of easily converting into a channel. It is most useful for types where mutability is not in question.

var Fibonacci Enumerable[uint] = fibonacciGenerator{}

Fibonacci is an Enumerable which will dynamically generate the fibonacci sequence.

func AsEnumerable

func AsEnumerable[T any](entries ...T) Enumerable[T]

AsEnumerable allows for easy conversion of a slice to a re-usable Enumerable object.

func Empty

func Empty[T any]() Enumerable[T]

func Merge

func Merge[T any](channels ...Enumerable[T]) Enumerable[T]

Merge takes the results as it receives them from several channels and directs them into a single channel.

Example
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

a := collection.AsEnumerable(1, 2, 4)
b := collection.AsEnumerable(8, 16, 32)
c := collection.Merge(a, b)
sum := 0
for x := range c.Enumerate(ctx) {
	sum += x
}
fmt.Println(sum)

product := 1
for y := range a.Enumerate(ctx) {
	product *= y
}
fmt.Println(product)
Output:

63
8

func ParallelSelect

func ParallelSelect[T any, E any](original Enumerable[T], operation Transform[T, E]) Enumerable[E]

ParallelSelect creates an Enumerable which will use all logically available CPUs to execute a Transform.

func Reverse

func Reverse[T any](original Enumerable[T]) Enumerable[T]

Reverse will enumerate all values of an enumerable, store them in a Stack, then replay them all.

func Select

func Select[T any, E any](subject Enumerable[T], transform Transform[T, E]) Enumerable[E]

Select creates a reusable stream of transformed values.

Example
const offset = 'a' - 1

subject := collection.AsEnumerable[rune]('a', 'b', 'c')
subject = collection.Select(subject, func(a rune) rune {
	return a - offset
})

fmt.Println(collection.ToSlice(subject))
Output:

[1 2 3]

func SelectMany

func SelectMany[T any, E any](subject Enumerable[T], toMany Unfolder[T, E]) Enumerable[E]

SelectMany allows for unfolding of values.

Example
type BrewHouse struct {
	Name  string
	Beers collection.Enumerable[string]
}

breweries := collection.AsEnumerable(
	BrewHouse{
		"Mac & Jacks",
		collection.AsEnumerable(
			"African Amber",
			"Ibis IPA",
		),
	},
	BrewHouse{
		"Post Doc",
		collection.AsEnumerable(
			"Prereq Pale",
		),
	},
	BrewHouse{
		"Resonate",
		collection.AsEnumerable(
			"Comfortably Numb IPA",
			"Lithium Altbier",
		),
	},
	BrewHouse{
		"Triplehorn",
		collection.AsEnumerable(
			"Samson",
			"Pepper Belly",
		),
	},
)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

beers := collection.SelectMany(breweries, func(brewer BrewHouse) collection.Enumerator[string] {
	return brewer.Beers.Enumerate(ctx)
})

for beer := range beers.Enumerate(ctx) {
	fmt.Println(beer)
}
Output:

African Amber
Ibis IPA
Prereq Pale
Comfortably Numb IPA
Lithium Altbier
Samson
Pepper Belly

func Skip

func Skip[T any](subject Enumerable[T], n uint) Enumerable[T]

Skip creates a reusable stream which will skip the first `n` elements before iterating over the rest of the elements in an Enumerable.

Example
trimmed := collection.Take(collection.Skip(collection.Fibonacci, 1), 3)
for entry := range trimmed.Enumerate(context.Background()) {
	fmt.Println(entry)
}
Output:

1
1
2

func Take

func Take[T any](subject Enumerable[T], n uint) Enumerable[T]

Take retreives just the first `n` elements from an Enumerable.

Example
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

taken := collection.Take(collection.Fibonacci, 4)
for entry := range taken.Enumerate(ctx) {
	fmt.Println(entry)
}
Output:

0
1
1
2

func TakeWhile

func TakeWhile[T any](subject Enumerable[T], criteria func(T, uint) bool) Enumerable[T]

TakeWhile creates a reusable stream which will halt once some criteria is no longer met.

Example
taken := collection.TakeWhile(collection.Fibonacci, func(x, n uint) bool {
	return x < 10
})

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

for entry := range taken.Enumerate(ctx) {
	fmt.Println(entry)
}
Output:

0
1
1
2
3
5
8

func Where

func Where[T any](original Enumerable[T], p Predicate[T]) Enumerable[T]

Where creates a reusable means of filtering a stream.

Example
nums := collection.EnumerableSlice[int]([]int{1, 2, 3, 4, 5})
results := collection.Where[int](nums, func(a int) bool {
	return a < 3
})
fmt.Println(collection.ToSlice(results))
Output:

[1 2]

type EnumerableSlice

type EnumerableSlice[T any] []T

func (EnumerableSlice[T]) Enumerate

func (f EnumerableSlice[T]) Enumerate(ctx context.Context) Enumerator[T]
Example
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// When a single value is provided, and it is an array or slice, each value in the array or slice is treated as an enumerable value.
originalInts := []int{1, 2, 3, 4, 5}
wrappedInts := collection.EnumerableSlice[int](originalInts)

for entry := range wrappedInts.Enumerate(ctx) {
	fmt.Print(entry)
}
fmt.Println()

// It's easy to convert arrays to slices for these enumerations as well.
originalStrings := [7]string{"red", "orange", "yellow", "green", "blue", "indigo", "violet"}
wrappedStrings := collection.EnumerableSlice[string](originalStrings[:])
for entry := range wrappedStrings.Enumerate(ctx) {
	fmt.Println(entry)
}
Output:

12345
red
orange
yellow
green
blue
indigo
violet

type Enumerator

type Enumerator[T any] <-chan T

Enumerator exposes a new syntax for querying familiar data structures.

func (Enumerator[T]) All

func (iter Enumerator[T]) All(p Predicate[T]) bool

All tests whether or not all items present meet a criteria.

func (Enumerator[T]) AsEnumerable

func (iter Enumerator[T]) AsEnumerable() Enumerable[T]

AsEnumerable stores the results of an Enumerator so the results can be enumerated over repeatedly.

func (Enumerator[T]) Count

func (iter Enumerator[T]) Count(p Predicate[T]) int

Count iterates over a list and keeps a running tally of the number of elements satisfy a predicate.

Example
subject := collection.AsEnumerable("str1", "str1", "str2")
count1 := subject.Enumerate(context.Background()).Count(func(a string) bool {
	return a == "str1"
})
fmt.Println(count1)
Output:

2

func (Enumerator[T]) CountAll

func (iter Enumerator[T]) CountAll() int

CountAll iterates over a list and keeps a running tally of how many it's seen.

Example
subject := collection.AsEnumerable('a', 'b', 'c', 'd', 'e')
fmt.Println(subject.Enumerate(context.Background()).CountAll())
Output:

5

func (Enumerator[T]) Discard

func (iter Enumerator[T]) Discard()

Discard reads an enumerator to the end but does nothing with it. This method should be used in circumstances when it doesn't make sense to explicitly cancel the Enumeration.

func (Enumerator[T]) ElementAt

func (iter Enumerator[T]) ElementAt(n uint) T

ElementAt retreives an item at a particular position in an Enumerator.

Example
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// ElementAt leaves the Enumerator open, creating a memory leak unless remediated,
// context.Context should be cancelled to indicate that no further reads are coming.
fmt.Print(collection.Fibonacci.Enumerate(ctx).ElementAt(4))
Output:

3

func (Enumerator[T]) Last

func (iter Enumerator[T]) Last() (retval T)

Last retreives the item logically behind all other elements in the list.

Example
subject := collection.AsEnumerable(1, 2, 3)
fmt.Print(subject.Enumerate(context.Background()).Last())
Output:

3

func (Enumerator[T]) Merge

func (iter Enumerator[T]) Merge(others ...Enumerator[T]) Enumerator[T]

Merge takes the results of this Enumerator and others, and funnels them into a single Enumerator. The order of in which they will be combined is non-deterministic.

func (Enumerator[T]) Reverse

func (iter Enumerator[T]) Reverse() Enumerator[T]

Reverse returns items in the opposite order it encountered them in.

Example
a := collection.AsEnumerable(1, 2, 3).Enumerate(context.Background())
a = a.Reverse()
fmt.Println(a.ToSlice())
Output:

[3 2 1]

func (Enumerator[T]) Skip

func (iter Enumerator[T]) Skip(n uint) Enumerator[T]

Skip retreives all elements after the first 'n' elements.

Example
subject := collection.AsEnumerable(1, 2, 3, 4, 5, 6, 7)
skipped := subject.Enumerate(context.Background()).Skip(5)
for entry := range skipped {
	fmt.Println(entry)
}
Output:

6
7

func (Enumerator[T]) Take

func (iter Enumerator[T]) Take(n uint) Enumerator[T]

Take retreives just the first 'n' elements from an Enumerator.

Example
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

taken := collection.Fibonacci.Enumerate(ctx).Skip(4).Take(2)
for entry := range taken {
	fmt.Println(entry)
}
Output:

3
5

func (Enumerator[T]) TakeWhile

func (iter Enumerator[T]) TakeWhile(criteria func(T, uint) bool) Enumerator[T]

TakeWhile continues returning items as long as 'criteria' holds true.

Example
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
taken := collection.Fibonacci.Enumerate(ctx).TakeWhile(func(x, n uint) bool {
	return x < 6
})
for entry := range taken {
	fmt.Println(entry)
}
Output:

0
1
1
2
3
5

func (Enumerator[T]) Tee

func (iter Enumerator[T]) Tee() (Enumerator[T], Enumerator[T])

Tee creates two Enumerators which will have identical contents as one another.

Example
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

base := collection.AsEnumerable(1, 2, 4)
left, right := base.Enumerate(ctx).Tee()
var wg sync.WaitGroup
wg.Add(2)

product := 1
go func() {
	for x := range left {
		product *= x
	}
	wg.Done()
}()

sum := 0
go func() {
	for x := range right {
		sum += x
	}
	wg.Done()
}()

wg.Wait()

fmt.Printf("Sum: %d\n", sum)
fmt.Printf("Product: %d\n", product)
Output:

Sum: 7
Product: 8

func (Enumerator[T]) ToSlice

func (iter Enumerator[T]) ToSlice() []T

ToSlice places all iterated over values in a Slice for easy consumption.

func (Enumerator[T]) UCount

func (iter Enumerator[T]) UCount(p Predicate[T]) uint

UCount iterates over a list and keeps a running tally of the number of elements satisfy a predicate.

Example
subject := collection.EnumerableSlice[string]([]string{"str1", "str1", "str2"})
count1 := subject.Enumerate(context.Background()).UCount(func(a string) bool {
	return a == "str1"
})
fmt.Println(count1)
Output:

2

func (Enumerator[T]) UCountAll

func (iter Enumerator[T]) UCountAll() uint

UCountAll iterates over a list and keeps a running tally of how many it's seen.

Example
subject := collection.EnumerableSlice[any]([]interface{}{'a', 2, "str1"})
fmt.Println(subject.Enumerate(context.Background()).UCountAll())
Output:

3

func (Enumerator[T]) Where

func (iter Enumerator[T]) Where(predicate Predicate[T]) Enumerator[T]

Where iterates over a list and returns only the elements that satisfy a predicate.

Example
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

results := collection.Fibonacci.Enumerate(ctx).Where(func(a uint) bool {
	return a > 8
}).Take(3)
fmt.Println(results.ToSlice())
Output:

[13 21 34]

type LRUCache

type LRUCache[K comparable, V any] struct {
	// contains filtered or unexported fields
}

LRUCache hosts up to a given number of items. When more are presented, the least recently used item is evicted from the cache.

Example
subject := collection.NewLRUCache[int, string](3)
subject.Put(1, "one")
subject.Put(2, "two")
subject.Put(3, "three")
subject.Put(4, "four")
fmt.Println(subject.Get(1))
fmt.Println(subject.Get(4))
Output:

false
four true

func NewLRUCache

func NewLRUCache[K comparable, V any](capacity uint) *LRUCache[K, V]

NewLRUCache creates an empty cache, which will accommodate the given number of items.

func (*LRUCache[K, V]) Enumerate

func (lru *LRUCache[K, V]) Enumerate(ctx context.Context) Enumerator[V]

Enumerate lists each value in the cache.

Example
subject := collection.NewLRUCache[int, string](3)
subject.Put(1, "one")
subject.Put(2, "two")
subject.Put(3, "three")
subject.Put(4, "four")

for key := range subject.Enumerate(context.Background()) {
	fmt.Println(key)
}
Output:

four
three
two

func (*LRUCache[K, V]) EnumerateKeys

func (lru *LRUCache[K, V]) EnumerateKeys(ctx context.Context) Enumerator[K]

EnumerateKeys lists each key in the cache.

Example
subject := collection.NewLRUCache[int, string](3)
subject.Put(1, "one")
subject.Put(2, "two")
subject.Put(3, "three")
subject.Put(4, "four")

for key := range subject.EnumerateKeys(context.Background()) {
	fmt.Println(key)
}
Output:

4
3
2

func (*LRUCache[K, V]) Get

func (lru *LRUCache[K, V]) Get(key K) (V, bool)

Get retrieves a cached value, if it is still present.

func (*LRUCache[K, V]) Put

func (lru *LRUCache[K, V]) Put(key K, value V)

Put adds a value to the cache. The added value may be expelled without warning.

func (*LRUCache[K, V]) Remove

func (lru *LRUCache[K, V]) Remove(key K) bool

Remove explicitly takes an item out of the cache.

type LinkedList

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

LinkedList encapsulates a list where each entry is aware of only the next entry in the list.

func NewLinkedList

func NewLinkedList[T any](entries ...T) *LinkedList[T]

NewLinkedList instantiates a new LinkedList with the entries provided.

Example
subject1 := collection.NewLinkedList('a', 'b', 'c', 'd', 'e')
fmt.Println(subject1.Length())

slice := []interface{}{1, 2, 3, 4, 5, 6}
subject2 := collection.NewLinkedList(slice...)
fmt.Println(subject2.Length())
Output:

5
6

func (*LinkedList[T]) AddBack

func (list *LinkedList[T]) AddBack(entry T)

AddBack creates an entry in the LinkedList that is logically at the back of the list.

Example
subject := collection.NewLinkedList(2, 3, 5)
subject.AddBack(8)
result, _ := subject.PeekBack()
fmt.Println(result)
fmt.Println(subject.Length())
Output:

8
4

func (*LinkedList[T]) AddFront

func (list *LinkedList[T]) AddFront(entry T)

AddFront creates an entry in the LinkedList that is logically at the front of the list.

Example
subject := collection.NewLinkedList(2, 3)
subject.AddFront(1)
result, _ := subject.PeekFront()
fmt.Println(result)
Output:

1

func (*LinkedList[T]) Enumerate

func (list *LinkedList[T]) Enumerate(ctx context.Context) Enumerator[T]

Enumerate creates a new instance of Enumerable which can be executed on.

Example
subject := collection.NewLinkedList(2, 3, 5, 8)
results := collection.Select[int](subject, func(a int) int {
	return -1 * a
})

for entry := range results.Enumerate(context.Background()) {
	fmt.Println(entry)
}
Output:

-2
-3
-5
-8

func (*LinkedList[T]) Get

func (list *LinkedList[T]) Get(pos uint) (T, bool)

Get finds the value from the LinkedList. pos is expressed as a zero-based index begining from the 'front' of the list.

Example
subject := collection.NewLinkedList(2, 3, 5, 8)
val, _ := subject.Get(2)
fmt.Println(val)
Output:

5

func (*LinkedList[T]) IsEmpty

func (list *LinkedList[T]) IsEmpty() bool

IsEmpty tests the list to determine if it is populate or not.

func (*LinkedList[T]) Length

func (list *LinkedList[T]) Length() uint

Length returns the number of elements present in the LinkedList.

func (*LinkedList[T]) PeekBack

func (list *LinkedList[T]) PeekBack() (T, bool)

PeekBack returns the entry logicall stored at the back of the list without removing it.

func (*LinkedList[T]) PeekFront

func (list *LinkedList[T]) PeekFront() (T, bool)

PeekFront returns the entry logically stored at the front of this list without removing it.

func (*LinkedList[T]) RemoveBack

func (list *LinkedList[T]) RemoveBack() (T, bool)

RemoveBack returns the entry logically stored at the back of this list and removes it.

func (*LinkedList[T]) RemoveFront

func (list *LinkedList[T]) RemoveFront() (T, bool)

RemoveFront returns the entry logically stored at the front of this list and removes it.

func (*LinkedList[T]) Sort

func (list *LinkedList[T]) Sort(comparator Comparator[T]) error

Sort rearranges the positions of the entries in this list so that they are ascending.

Example
// Sorti sorts into ascending order, this example demonstrates sorting
// into descending order.
subject := collection.NewLinkedList(2, 4, 3, 5, 7, 7)
subject.Sort(func(a, b int) (int, error) {
	return b - a, nil
})
fmt.Println(subject)
Output:

[7 7 5 4 3 2]

func (*LinkedList[T]) String

func (list *LinkedList[T]) String() string

String prints upto the first fifteen elements of the list in string format.

Example
subject1 := collection.NewLinkedList[int]()
for i := 0; i < 20; i++ {
	subject1.AddBack(i)
}
fmt.Println(subject1)

subject2 := collection.NewLinkedList[int](1, 2, 3)
fmt.Println(subject2)
Output:

[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ...]
[1 2 3]

func (*LinkedList[T]) Swap

func (list *LinkedList[T]) Swap(x, y uint) error

Swap switches the positions in which two values are stored in this list. x and y represent the indexes of the items that should be swapped.

Example
subject := collection.NewLinkedList(2, 3, 5, 8, 13)
subject.Swap(1, 3)
fmt.Println(subject)
Output:

[2 8 5 3 13]

func (*LinkedList[T]) ToSlice

func (list *LinkedList[T]) ToSlice() []T

ToSlice converts the contents of the LinkedList into a slice.

type List

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

List is a dynamically sized list akin to List in the .NET world, ArrayList in the Java world, or vector in the C++ world.

func NewList

func NewList[T any](entries ...T) *List[T]

NewList creates a new list which contains the elements provided.

func (*List[T]) Add

func (l *List[T]) Add(entries ...T)

Add appends an entry to the logical end of the List.

func (*List[T]) AddAt

func (l *List[T]) AddAt(pos uint, entries ...T)

AddAt injects values beginning at `pos`. If multiple values are provided in `entries` they are placed in the same order they are provided.

Example
subject := NewList(0, 1, 4, 5, 6)
subject.AddAt(2, 2, 3)
fmt.Println(subject)
Output:

[0 1 2 3 4 5 6]

func (*List[T]) Enumerate

func (l *List[T]) Enumerate(ctx context.Context) Enumerator[T]

Enumerate lists each element present in the collection

func (*List[T]) Get

func (l *List[T]) Get(pos uint) (T, bool)

Get retreives the value stored in a particular position of the list. If no item exists at the given position, the second parameter will be returned as false.

func (*List[T]) IsEmpty

func (l *List[T]) IsEmpty() bool

IsEmpty tests to see if this List has any elements present.

func (*List[T]) Length

func (l *List[T]) Length() uint

Length returns the number of elements in the List.

func (*List[T]) Remove

func (l *List[T]) Remove(pos uint) (T, bool)

Remove retreives a value from this List and shifts all other values.

func (*List[T]) Set

func (l *List[T]) Set(pos uint, val T) bool

Set updates the value stored at a given position in the List.

func (*List[T]) String

func (l *List[T]) String() string

String generates a textual representation of the List for the sake of debugging.

func (*List[T]) Swap

func (l *List[T]) Swap(x, y uint) bool

Swap switches the values that are stored at positions `x` and `y`

type Predicate

type Predicate[T any] func(T) bool

Predicate defines an interface for funcs that make some logical test.

type Queue

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

Queue implements a basic FIFO structure.

func NewQueue

func NewQueue[T any](entries ...T) *Queue[T]

NewQueue instantiates a new FIFO structure.

Example
empty := NewQueue[int]()
fmt.Println(empty.Length())

populated := NewQueue(1, 2, 3, 5, 8, 13)
fmt.Println(populated.Length())
Output:

0
6

func (*Queue[T]) Add

func (q *Queue[T]) Add(entry T)

Add places an item at the back of the Queue.

Example
subject := &Queue[int]{}
subject.Add(1)
subject.Add(2)
res, _ := subject.Peek()
fmt.Println(res)
Output:

1

func (*Queue[T]) Enumerate

func (q *Queue[T]) Enumerate(ctx context.Context) Enumerator[T]

Enumerate peeks at each element of this queue without mutating it.

func (*Queue[T]) IsEmpty

func (q *Queue[T]) IsEmpty() bool

IsEmpty tests the Queue to determine if it is populate or not.

Example
empty := NewQueue[int]()
fmt.Println(empty.IsEmpty())

populated := NewQueue(1, 2, 3, 5, 8, 13)
fmt.Println(populated.IsEmpty())
Output:

true
false

func (*Queue[T]) Length

func (q *Queue[T]) Length() uint

Length returns the number of items in the Queue.

func (*Queue[T]) Next

func (q *Queue[T]) Next() (T, bool)

Next removes and returns the next item in the Queue.

Example
subject := NewQueue(1, 2, 3, 5, 8, 13)
for !subject.IsEmpty() {
	val, _ := subject.Next()
	fmt.Println(val)
}
Output:

1
2
3
5
8
13

func (*Queue[T]) Peek

func (q *Queue[T]) Peek() (T, bool)

Peek returns the next item in the Queue without removing it.

func (*Queue[T]) ToSlice

func (q *Queue[T]) ToSlice() []T

ToSlice converts a Queue into a slice.

type Stack

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

Stack implements a basic FILO structure.

func NewStack

func NewStack[T any](entries ...T) *Stack[T]

NewStack instantiates a new FILO structure.

Example
subject := NewStack(1, 2, 3)
for !subject.IsEmpty() {
	val, _ := subject.Pop()
	fmt.Println(val)
}
Output:

3
2
1

func (*Stack[T]) Enumerate

func (stack *Stack[T]) Enumerate(ctx context.Context) Enumerator[T]

Enumerate peeks at each element in the stack without mutating it.

func (*Stack[T]) IsEmpty

func (stack *Stack[T]) IsEmpty() bool

IsEmpty tests the Stack to determine if it is populate or not.

func (*Stack[T]) Peek

func (stack *Stack[T]) Peek() (T, bool)

Peek returns the entry at the top of the Stack without removing it.

func (*Stack[T]) Pop

func (stack *Stack[T]) Pop() (T, bool)

Pop returns the entry at the top of the Stack then removes it.

func (*Stack[T]) Push

func (stack *Stack[T]) Push(entry T)

Push adds an entry to the top of the Stack.

func (*Stack[T]) Size

func (stack *Stack[T]) Size() uint

Size returns the number of entries populating the Stack.

type Transform

type Transform[T any, E any] func(T) E

Transform defines a function which takes a value, and returns some value based on the original.

func Identity

func Identity[T any]() Transform[T, T]

Identity returns a trivial Transform which applies no operation on the value.

type Unfolder

type Unfolder[T any, E any] func(T) Enumerator[E]

Unfolder defines a function which takes a single value, and exposes many of them as an Enumerator

Jump to

Keyboard shortcuts

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