pipez

package
v1.2.2 Latest Latest
Warning

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

Go to latest
Published: Feb 13, 2026 License: MIT Imports: 1 Imported by: 0

README

pipez

Fluent, chainable API for slice operations

The pipez package wraps the slicez functions in a method-chaining API. It's implemented as a type alias (zero overhead) that provides a more readable, pipeline-style interface.

Quick Reference

All methods chain and return Pipe[A] except terminal operations which return concrete values:

Chain Methods (return Pipe[A]):

  • Transform: Map, Reverse, Shuffle
  • Filter: Filter, Reject, Take, Drop
  • Access: Tail, Initial
  • Combine: Concat, Zip, Interleave

Terminal Operations (return values):

  • Slice() - Get underlying []A
  • Head() - Get first element (A, error)
  • Last() - Get last element (A, error)
  • Count() - Get length int
  • Nth() - Get element at index A
  • Every(), Some(), None() - Predicates bool
  • Fold(), FoldRight() - Reduce to value
  • Partition() - Split into two ([]A, []A)

Installation

go get github.com/modfin/henry/pipez

Usage

import "github.com/modfin/henry/pipez"

// Traditional approach
result := slicez.Take(
    slicez.Filter(
        slicez.Map(nums, func(n int) int { return n * 2 }),
        func(n int) bool { return n > 10 },
    ),
    3,
)

// Fluent approach
result := pipez.Of(nums).
    Map(func(n int) int { return n * n }).
    Filter(func(n int) bool { return n > 10 }).
    Take(3).
    Slice()

Creating Pipes

Of

Create a pipe from a slice.

pipe := pipez.Of([]int{1, 2, 3, 4, 5})

Chain Methods

Transformation
Map

Transform each element.

result := pipez.Of([]int{1, 2, 3}).
    Map(func(n int) int { return n * n }).
    Slice()
// result = []int{1, 4, 9}
Reverse

Reverse order.

result := pipez.Of([]int{1, 2, 3}).
    Reverse().
    Slice()
// result = []int{3, 2, 1}
Shuffle

Randomize order.

result := pipez.Of([]int{1, 2, 3, 4, 5}).
    Shuffle().
    Slice()
// result = []int{4, 1, 5, 2, 3} (random)
Filtering
Filter

Keep matching elements.

result := pipez.Of([]int{1, 2, 3, 4, 5, 6}).
    Filter(func(n int) bool { return n%2 == 0 }).
    Slice()
// result = []int{2, 4, 6}
Reject

Remove matching elements.

result := pipez.Of([]int{1, 2, 3, 4, 5, 6}).
    Reject(func(n int) bool { return n%2 == 0 }).
    Slice()
// result = []int{1, 3, 5}
Take

Take first N.

result := pipez.Of([]int{1, 2, 3, 4, 5}).
    Take(3).
    Slice()
// result = []int{1, 2, 3}
TakeRight

Take last N.

result := pipez.Of([]int{1, 2, 3, 4, 5}).
    TakeRight(2).
    Slice()
// result = []int{4, 5}
Drop

Drop first N.

result := pipez.Of([]int{1, 2, 3, 4, 5}).
    Drop(2).
    Slice()
// result = []int{3, 4, 5}
DropRight

Drop last N.

result := pipez.Of([]int{1, 2, 3, 4, 5}).
    DropRight(2).
    Slice()
// result = []int{1, 2, 3}
TakeWhile

Take while predicate true.

result := pipez.Of([]int{1, 2, 3, 4, 5}).
    TakeWhile(func(n int) bool { return n < 4 }).
    Slice()
// result = []int{1, 2, 3}
TakeRightWhile

Take from right while true.

result := pipez.Of([]int{1, 2, 3, 4, 5}).
    TakeRightWhile(func(n int) bool { return n > 2 }).
    Slice()
// result = []int{3, 4, 5}
DropWhile

Drop while predicate true.

result := pipez.Of([]int{1, 2, 3, 4, 5}).
    DropWhile(func(n int) bool { return n < 4 }).
    Slice()
// result = []int{4, 5}
DropRightWhile

Drop from right while true.

result := pipez.Of([]int{1, 2, 3, 4, 5}).
    DropRightWhile(func(n int) bool { return n > 3 }).
    Slice()
// result = []int{1, 2, 3}
Combination
Concat

Append slices.

result := pipez.Of([]int{1, 2, 3}).
    Concat([]int{4, 5}, []int{6, 7, 8}).
    Slice()
// result = []int{1, 2, 3, 4, 5, 6, 7, 8}
Zip

Combine with another slice.

result := pipez.Of([]int{1, 2, 3}).
    Zip([]int{10, 20, 30}, func(a, b int) int {
        return a + b
    }).
    Slice()
// result = []int{11, 22, 33}
Interleave

Interleave with other slices.

result := pipez.Of([]int{1, 4, 7}).
    Interleave([]int{2, 5, 8}, []int{3, 6, 9}).
    Slice()
// result = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
Side Effects
Peek

Apply function without changing values.

result := pipez.Of([]int{1, 2, 3}).
    Peek(func(n int) { fmt.Printf("Processing %d\n", n) }).
    Map(func(n int) int { return n * 2 }).
    Slice()
// Prints: Processing 1, Processing 2, Processing 3
// result = []int{2, 4, 6}

Terminal Operations

Slice

Get the underlying slice.

underlying := pipe.Slice()
Head

Get first element.

first, err := pipe.Head()
// Returns error if empty
Last

Get last element.

last, err := pipe.Last()
// Returns error if empty
Count

Get length.

n := pipe.Count()
Nth

Get element at index.

elem := pipe.Nth(2)  // 3rd element (0-indexed)
Every / Some / None

Check predicates.

allPositive := pipe.Every(func(n int) bool { return n > 0 })
hasEven := pipe.Some(func(n int) bool { return n%2 == 0 })
noNegatives := pipe.None(func(n int) bool { return n < 0 })
Fold / FoldRight

Reduce to value.

sum := pipe.Fold(func(acc, n int) int { return acc + n }, 0)
Partition

Split into two slices.

evens, odds := pipe.Partition(func(n int) bool { return n%2 == 0 })
Compact

Remove consecutive duplicates.

result := pipez.Of([]int{1, 1, 2, 2, 2, 3}).
    Compact(func(a, b int) bool { return a == b }).
    Slice()
// result = []int{1, 2, 3}
Sample

Get random sample.

result := pipez.Of([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}).
    Sample(3).
    Slice()
// result = 3 random elements
SortFunc

Sort with custom comparator.

result := pipez.Of([]int{3, 1, 4, 1, 5}).
    SortFunc(func(a, b int) bool { return a < b }).
    Slice()
// result = []int{1, 1, 3, 4, 5}

Complex Example

// Process user data
type User struct {
    Name string
    Age  int
}

users := []User{
    {"Alice", 30},
    {"Bob", 25},
    {"Charlie", 35},
    {"David", 20},
}

// Get names of adults, sorted, take top 3
adultNames := pipez.Of(users).
    Filter(func(u User) bool { return u.Age >= 18 }).
    SortFunc(func(a, b User) bool { return a.Age > b.Age }).
    Map(func(u User) string { return u.Name }).
    Take(3).
    Slice()

// adultNames = []string{"Charlie", "Alice", "Bob"}

Performance

Pipe[A] is a type alias for []A, so there's zero overhead:

type Pipe[A any] []A

// Direct conversion
var slice []int = []int{1, 2, 3}
pipe := pipez.Of(slice)  // Just a cast
back := pipe.Slice()       // Just a cast

When to Use

Use pipez when:

  • Building complex data pipelines
  • Readability is more important than raw performance
  • You want to chain 3+ operations

Use slicez when:

  • You need maximum performance
  • Doing single operations
  • Working in tight loops

See Also

  • slicez - Underlying function implementations
  • chanz - For asynchronous pipelines

Documentation

Overview

Package pipez provides a fluent, chainable API for slice operations.

Pipe wraps slices and provides method chaining for functional programming operations. This allows for readable, pipeline-style data transformations:

result := pipez.Of([]int{1, 2, 3, 4, 5}).
    Filter(func(n int) bool { return n > 2 }).
    Map(func(n int) int { return n * 2 }).
    Slice()
// result = []int{6, 8, 10}

Most methods return Pipe for continued chaining. Terminal operations like Head(), Last(), and Fold() return concrete values and break the chain.

The pipez package is built on top of slicez, providing the same operations with a method-chaining interface instead of standalone functions.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Pipe

type Pipe[A any] []A

Pipe is a type alias for []A that provides method chaining for slice operations. It wraps slicez functions in a fluent API, allowing operations to be chained together.

Pipe is not a new type but a type alias, so there's zero overhead - it's just a slice with methods attached. You can convert between []A and Pipe[A] freely.

Example:

data := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result := pipez.Of(data).
    Filter(func(n int) bool { return n > 5 }).
    Map(func(n int) int { return n * n }).
    Take(3).
    Slice()
// result = []int{36, 49, 64} (6², 7², 8²)

func Of

func Of[A any](a []A) Pipe[A]

Of creates a Pipe from a slice, enabling method chaining.

Example:

pipe := pipez.Of([]int{1, 2, 3, 4, 5})
result := pipe.Filter(func(n int) bool { return n%2 == 0 }).Slice()
// result = []int{2, 4}

func (Pipe[A]) Compact

func (p Pipe[A]) Compact(equal func(a, b A) bool) Pipe[A]

Compact removes consecutive duplicate elements using the equality function. Only removes duplicates that are adjacent; non-consecutive duplicates are kept.

Example:

pipez.Of([]int{1, 1, 2, 2, 2, 3, 3}).Compact(func(a, b int) bool { return a == b }).Slice()
// Returns []int{1, 2, 3}

func (Pipe[A]) Concat

func (p Pipe[A]) Concat(slices ...[]A) Pipe[A]

Concat appends additional slices to the pipe, returning a new Pipe.

Example:

pipez.Of([]int{1, 2}).Concat([]int{3, 4}, []int{5, 6}).Slice()
// Returns []int{1, 2, 3, 4, 5, 6}

func (Pipe[A]) Count

func (p Pipe[A]) Count() int

Count returns the number of elements in the pipe. This is a terminal operation that breaks the chain.

Example:

count := pipez.Of([]int{1, 2, 3, 4, 5}).Count()
// count = 5

func (Pipe[A]) Drop

func (p Pipe[A]) Drop(i int) Pipe[A]

Drop removes the first i elements, returning a new Pipe with the remainder. Returns empty pipe if i >= length.

Example:

pipez.Of([]int{1, 2, 3, 4, 5}).Drop(2).Slice()
// Returns []int{3, 4, 5}

func (Pipe[A]) DropRight

func (p Pipe[A]) DropRight(i int) Pipe[A]

DropRight removes the last i elements, returning a new Pipe with the remainder. Returns empty pipe if i >= length.

Example:

pipez.Of([]int{1, 2, 3, 4, 5}).DropRight(2).Slice()
// Returns []int{1, 2, 3}

func (Pipe[A]) DropRightWhile

func (p Pipe[A]) DropRightWhile(drop func(a A) bool) Pipe[A]

DropRightWhile removes elements from the end while the predicate is true. Returns elements up to the last one where predicate returns false.

Example:

pipez.Of([]int{1, 2, 3, 4, 5}).DropRightWhile(func(n int) bool { return n > 3 }).Slice()
// Returns []int{1, 2, 3}

func (Pipe[A]) DropWhile

func (p Pipe[A]) DropWhile(drop func(a A) bool) Pipe[A]

DropWhile removes elements from the start while the predicate is true. Returns elements starting from the first one where predicate returns false.

Example:

pipez.Of([]int{1, 2, 3, 4, 5}).DropWhile(func(n int) bool { return n < 4 }).Slice()
// Returns []int{4, 5}

func (Pipe[A]) Every

func (p Pipe[A]) Every(predicate func(a A) bool) bool

Every returns true if all elements satisfy the predicate. Returns true for empty pipes (vacuous truth). This is a terminal operation that breaks the chain.

Example:

allPositive := pipez.Of([]int{1, 2, 3}).Every(func(n int) bool { return n > 0 })
// allPositive = true

allEven := pipez.Of([]int{1, 2, 3}).Every(func(n int) bool { return n%2 == 0 })
// allEven = false

func (Pipe[A]) Filter

func (p Pipe[A]) Filter(include func(a A) bool) Pipe[A]

Filter returns elements that satisfy the predicate, removing others.

Example:

pipez.Of([]int{1, 2, 3, 4, 5}).Filter(func(n int) bool { return n%2 == 0 }).Slice()
// Returns []int{2, 4} (only even numbers)

func (Pipe[A]) Fold

func (p Pipe[A]) Fold(combined func(accumulator A, val A) A, accumulator A) A

Fold reduces the pipe from left to right, accumulating a result. This is a terminal operation that breaks the chain.

Example:

sum := pipez.Of([]int{1, 2, 3, 4}).Fold(func(acc, val int) int { return acc + val }, 0)
// sum = 10

func (Pipe[A]) FoldRight

func (p Pipe[A]) FoldRight(combined func(accumulator A, val A) A, accumulator A) A

FoldRight reduces the pipe from right to left, accumulating a result. This is a terminal operation that breaks the chain.

Example:

result := pipez.Of([]string{"a", "b", "c"}).FoldRight(func(acc, val string) string {
    if acc == "" { return val }
    return val + "-" + acc
}, "")
// result = "a-b-c"

func (Pipe[A]) Head

func (p Pipe[A]) Head() (A, error)

Head returns the first element and an error if the pipe is empty. This is a terminal operation that breaks the chain.

Example:

first, err := pipez.Of([]int{1, 2, 3}).Head()
// first = 1, err = nil

_, err := pipez.Of([]int{}).Head()
// err != nil (empty slice error)

func (Pipe[A]) Initial

func (p Pipe[A]) Initial() Pipe[A]

Initial returns all elements except the last, returning a new Pipe. Returns empty pipe if input has 0 or 1 elements.

Example:

pipez.Of([]int{1, 2, 3, 4}).Initial().Slice()
// Returns []int{1, 2, 3}

func (Pipe[A]) Interleave

func (p Pipe[A]) Interleave(a ...[]A) Pipe[A]

Interleave interleaves this pipe with additional slices round-robin style. Takes first element from each, then second from each, etc.

Example:

pipez.Of([]int{1, 5, 9}).Interleave([]int{2, 6, 10}, []int{3, 7, 11}).Slice()
// Returns []int{1, 2, 3, 5, 6, 7, 9, 10, 11}

func (Pipe[A]) Last

func (p Pipe[A]) Last() (A, error)

Last returns the last element and an error if the pipe is empty. This is a terminal operation that breaks the chain.

Example:

last, err := pipez.Of([]int{1, 2, 3}).Last()
// last = 3, err = nil

func (Pipe[A]) Map

func (p Pipe[A]) Map(f func(a A) A) Pipe[A]

Map transforms each element using the provided function.

Example:

pipez.Of([]int{1, 2, 3}).Map(func(n int) int { return n * n }).Slice()
// Returns []int{1, 4, 9}

func (Pipe[A]) None

func (p Pipe[A]) None(predicate func(a A) bool) bool

None returns true if no elements satisfy the predicate. Returns true for empty pipes. This is a terminal operation that breaks the chain.

Example:

noNegatives := pipez.Of([]int{1, 2, 3}).None(func(n int) bool { return n < 0 })
// noNegatives = true

func (Pipe[A]) Nth

func (p Pipe[A]) Nth(i int) A

Nth returns the element at index i (0-based), panicking if out of bounds. This is a terminal operation that breaks the chain.

Example:

elem := pipez.Of([]int{10, 20, 30}).Nth(1)
// elem = 20

func (Pipe[A]) Partition

func (p Pipe[A]) Partition(predicate func(a A) bool) (satisfied, notSatisfied []A)

Partition splits the pipe into two slices based on a predicate. First slice contains elements where predicate is true, second where false. This is a terminal operation that returns two slices (not a Pipe).

Example:

even, odd := pipez.Of([]int{1, 2, 3, 4, 5}).Partition(func(n int) bool { return n%2 == 0 })
// even = []int{2, 4}, odd = []int{1, 3, 5}

func (Pipe[A]) Peek

func (p Pipe[A]) Peek(apply func(a A)) Pipe[A]

Peek applies a function to each element for side effects, then returns the Pipe for chaining. Useful for logging, debugging, or performing actions without transforming data.

Example:

pipez.Of([]int{1, 2, 3}).
    Peek(func(n int) { fmt.Println("Processing:", n) }).
    Map(func(n int) int { return n * 2 }).
    Slice()
// Prints "Processing: 1", "Processing: 2", "Processing: 3" during execution

func (Pipe[A]) Reject

func (p Pipe[A]) Reject(exclude func(a A) bool) Pipe[A]

Reject returns elements that do NOT satisfy the predicate (inverse of Filter).

Example:

pipez.Of([]int{1, 2, 3, 4, 5}).Reject(func(n int) bool { return n%2 == 0 }).Slice()
// Returns []int{1, 3, 5} (only odd numbers)

func (Pipe[A]) Reverse

func (p Pipe[A]) Reverse() Pipe[A]

Reverse returns a new Pipe with elements in reverse order.

Example:

pipez.Of([]int{1, 2, 3}).Reverse().Slice()
// Returns []int{3, 2, 1}

func (Pipe[A]) Sample

func (p Pipe[A]) Sample(n int) Pipe[A]

Sample returns n random elements from the pipe. Uses efficient algorithms (Fisher-Yates, swap-to-end) for uniform sampling.

Example:

pipez.Of([]int{1, 2, 3, 4, 5}).Sample(3).Slice()
// Might return []int{2, 5, 1} (3 random elements)

func (Pipe[A]) Shuffle

func (p Pipe[A]) Shuffle() Pipe[A]

Shuffle returns a new Pipe with elements in random order. Uses Fisher-Yates shuffle for uniform distribution.

Example:

pipez.Of([]int{1, 2, 3, 4, 5}).Shuffle().Slice()
// Might return []int{3, 1, 5, 2, 4}

func (Pipe[A]) Slice

func (p Pipe[A]) Slice() []A

Slice returns the underlying slice, breaking the chain. Use this at the end of a pipeline to get the final []A result.

Example:

pipe := pipez.Of([]int{1, 2, 3}).Map(func(n int) int { return n * 2 })
slice := pipe.Slice()
// slice = []int{2, 4, 6}

func (Pipe[A]) Some

func (p Pipe[A]) Some(predicate func(a A) bool) bool

Some returns true if at least one element satisfies the predicate. Returns false for empty pipes. This is a terminal operation that breaks the chain.

Example:

hasEven := pipez.Of([]int{1, 2, 3}).Some(func(n int) bool { return n%2 == 0 })
// hasEven = true

func (Pipe[A]) SortFunc

func (p Pipe[A]) SortFunc(less func(a, b A) bool) Pipe[A]

SortFunc sorts the pipe using a custom comparison function. The less function should return true if a should come before b.

Example:

pipez.Of([]int{3, 1, 4, 1, 5}).SortFunc(func(a, b int) bool { return a < b }).Slice()
// Returns []int{1, 1, 3, 4, 5}

func (Pipe[A]) Tail

func (p Pipe[A]) Tail() Pipe[A]

Tail returns all elements except the first, returning a new Pipe. Returns empty pipe if input has 0 or 1 elements.

Example:

pipez.Of([]int{1, 2, 3, 4}).Tail().Slice()
// Returns []int{2, 3, 4}

func (Pipe[A]) Take

func (p Pipe[A]) Take(i int) Pipe[A]

Take returns the first i elements, returning a new Pipe. Returns all elements if i > length.

Example:

pipez.Of([]int{1, 2, 3, 4, 5}).Take(3).Slice()
// Returns []int{1, 2, 3}

func (Pipe[A]) TakeRight

func (p Pipe[A]) TakeRight(i int) Pipe[A]

TakeRight returns the last i elements, returning a new Pipe. Returns all elements if i > length.

Example:

pipez.Of([]int{1, 2, 3, 4, 5}).TakeRight(3).Slice()
// Returns []int{3, 4, 5}

func (Pipe[A]) TakeRightWhile

func (p Pipe[A]) TakeRightWhile(take func(a A) bool) Pipe[A]

TakeRightWhile returns elements from the end while the predicate is true. Stops at the first element from the end where predicate returns false.

Example:

pipez.Of([]int{1, 2, 3, 4, 5}).TakeRightWhile(func(n int) bool { return n > 3 }).Slice()
// Returns []int{4, 5}

func (Pipe[A]) TakeWhile

func (p Pipe[A]) TakeWhile(take func(a A) bool) Pipe[A]

TakeWhile returns elements from the start while the predicate is true. Stops at the first element where predicate returns false.

Example:

pipez.Of([]int{1, 2, 3, 4, 5}).TakeWhile(func(n int) bool { return n < 4 }).Slice()
// Returns []int{1, 2, 3}

func (Pipe[A]) Unzip

func (p Pipe[A]) Unzip(unzipper func(a A) (A, A)) ([]A, []A)

Unzip splits this pipe of pairs into two separate slices. This is a terminal operation that returns two slices (not a Pipe).

Example:

pairs := []struct{ X, Y int }{{1, 10}, {2, 20}, {3, 30}}
xs, ys := pipez.Of(pairs).Unzip(func(p struct{ X, Y int }) (int, int) { return p.X, p.Y })
// xs = []int{1, 2, 3}, ys = []int{10, 20, 30}

func (Pipe[A]) Zip

func (p Pipe[A]) Zip(b []A, zipper func(a, b A) A) Pipe[A]

Zip combines this pipe with another slice element-wise using a zipper function. Stops at the length of the shorter collection.

Example:

pipez.Of([]int{1, 2, 3}).Zip([]int{10, 20, 30}, func(a, b int) int { return a + b }).Slice()
// Returns []int{11, 22, 33}

Jump to

Keyboard shortcuts

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