slice

package
v0.27.0 Latest Latest
Warning

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

Go to latest
Published: Mar 1, 2026 License: MIT Imports: 5 Imported by: 0

README

slice

Replace loop scaffolding with type-safe collection chains.

  • InterchangeableMapper[T] is []T. Index, range, append, len all work. Pass to or return from any function expecting []T.
  • Generics — 100% type-safe. No any, no reflection, no type assertions.
  • Method expressions — pass User.IsActive directly. No wrapper closures.
  • Comma-okFind, IndexWhere return option with .Get()(value, ok).
// Before: 3 lines of scaffolding, 2 closing braces, 1 line of intent
var names []string
for _, u := range users {
    if u.IsActive() {
        names = append(names, u.Name)
    }
}

// After: intent only
names := slice.From(users).KeepIf(User.IsActive).ToString(User.GetName)

Six lines become one.

What It Looks Like

// Ranking
top5 := slice.SortByDesc(players, Player.Score).TakeFirst(5)
// Tag filtering (allowlist semantics — empty filter matches all)
if !slice.String(m.Tags).Matches(filter.Tags) {
    continue
}
// Type mapping
users := slice.MapTo[User](ids).Map(FetchUser)
// Multi-field extraction in one pass
prices, quantities, discounts, taxes := slice.Unzip4(orders,
    Order.Price, Order.Quantity, Order.Discount, Order.Tax,
)

Order.Price is a method expression — Go turns the method into a func(Order) float64, which is exactly what Unzip expects. This works when your types have accessor methods. Without them, named functions work in the same position:

// With accessor methods
prices, qtys := slice.Unzip2(orders, Order.Price, Order.Quantity)

// Without — named functions instead
getPrice := func(o Order) float64 { return o.Price() }
getQty := func(o Order) int { return o.Qty() }
prices, qtys := slice.Unzip2(orders, getPrice, getQty)

Method expressions read as intent at the call site — Order.Price vs reading a function body. They pay off when multiple sites extract the same field. For one-off use, named functions avoid the extra method.

// Set construction for O(1) lookup — works with any comparable type
allowed := slice.ToSet(cfg.AllowedRoles)
if allowed[user.Role] {
    grant(user)
}
// Expand each department into its members — one-to-many, then flatten
allEmployees := slice.From(departments).FlatMap(Department.Employees)
// Reduce to map
byMAC := slice.Fold(devices, make(map[string]Device), addDevice)
// Number items starting from 1 — state is the counter, output is the labeled string
// number returns the next counter and a formatted label.
number := func(n int, item Item) (int, string) {
    return n + 1, fmt.Sprintf("%d. %s", n, item.Name)
}
_, numbered := slice.MapAccum(items, 1, number)
// ["1. Apples", "2. Bread", "3. Milk"]

It's Just a Slice

Mapper[T] is []T. Use it anywhere you'd use a slice:

func activeNames(users []User) []string {
    names := slice.From(users).KeepIf(User.IsActive).ToString(User.Name)
    names.Each(lof.Println)
    return names  // return as []string — no conversion needed
}
result := slice.From(users).KeepIf(User.IsActive)
fmt.Println(result[0])         // index
fmt.Println(len(result))       // len
result = append(result, extra) // append
for _, u := range result {     // range
    process(u)
}
  • Keep []T in your function signatures, not Mapper[T] — use From() at the point of use. This keeps fluentfp as an implementation detail; callers don't need to import it.
  • From() is a type conversion (copies the slice header, shares the backing array — append to either may mutate the other if capacity remains)
  • Nil-safe: From(nil).KeepIf(...).ToString(...) returns an empty slice — Go's range over nil is zero iterations

Other Go FP libraries can't do this:

  • go-linq, fuego: 6+ lines of []any[]string with type assertions to get results out
  • gofp: conversion loops on both ends — []User[]any in, []any[]string out

See comparison for the full library comparison.

Operations

From creates Mapper[T]. MapTo[R] creates MapperTo[R,T] — all Mapper methods plus Map for arbitrary type mapping. String ([]string), Int ([]int), and Float64 ([]float64) are separate defined types with additional methods.

  • Filter: KeepIf, RemoveIf, TakeFirst
  • Search: Find, IndexWhere, FindAs, Any, First, Single, Contains, ContainsAny, Matches (String)
  • Transform: Convert, FlatMap, Map (MapperTo), ToString, ToInt, other To*, Clone, Unique (String), SortBy, SortByDesc
  • Aggregate: Fold, MapAccum, Len, Max (Int, Float64), Min (Int, Float64), Sum (Int, Float64), ToSet, Each, Unzip2/3/4
  • Parallel: ParallelMap, ParallelKeepIf, ParallelEach

Fold, not Reduce: Fold takes an initial value and allows the return type to differ from the element type (func(R, T) R). Reduce conventionally implies no initial value and same-type accumulation. The name matches the semantics.

Parallel Operations

Same API, concurrent execution. The pure-function contract (no shared state in predicates or transforms) makes parallelism safe by construction.

// CPU-bound transform — 4x speedup at 10k elements
scores := slice.ParallelMap(users, runtime.GOMAXPROCS(0), ComputeScore)

// Parallel filter chains naturally into sequential operations
active := slice.From(users).ParallelKeepIf(4, User.IsActive).ToString(User.Name)

Parallel overhead only pays off when fn does meaningful work per element. Pure field access won't benefit — CPU-bound transforms and I/O-bound operations will. Benchmarks:

Operation Work 10k elements Speedup
ParallelMap trivial (n*2) ~2x slower
ParallelMap CPU-bound (50 sin/cos) ~5x faster yes
ParallelKeepIf trivial (n%2) ~4x slower
ParallelKeepIf CPU-bound ~4x faster yes

Run go test -bench=BenchmarkParallel -benchmem ./slice/ for numbers on your hardware.

Edge cases: workers <= 0 panics, workers == 1 runs sequentially (no goroutine overhead), workers > len clamps, nil/empty input returns empty (not nil).

See pkg.go.dev for complete API documentation and the main README for installation and performance characteristics.

Documentation

Overview

Package slice provides fluent slice types that can chain functional collection operations.

Mapper[T] is a fluent slice that can chain operations like ToString (map), KeepIf (filter), etc.

MapperTo[T, R] is a fluent slice with one additional method, MapTo, for mapping to a specified type R. If you don't need to map to an arbitrary type, use Mapper instead.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func FindAs added in v0.21.0

func FindAs[R, T any](ts []T) option.Basic[R]

FindAs returns the first element that type-asserts to R, or not-ok if none match. Useful for finding a specific concrete type in a slice of interfaces.

func Fold added in v0.6.0

func Fold[T, R any](ts []T, initial R, fn func(R, T) R) R

Fold reduces a slice to a single value by applying fn to each element. It starts with initial and applies fn(accumulator, element) for each element from left to right. Returns initial if the slice is empty.

func ToSet added in v0.23.0

func ToSet[T comparable](ts []T) map[T]bool

ToSet returns a map with each element as a key set to true. Requires comparable elements.

func Unzip2 added in v0.6.0

func Unzip2[T, A, B any](ts []T, fa func(T) A, fb func(T) B) (Mapper[A], Mapper[B])

Unzip2 extracts two slices from ts in a single pass by applying the extraction functions. This is more efficient than calling two separate mapping operations when you need multiple fields.

func Unzip3 added in v0.6.0

func Unzip3[T, A, B, C any](ts []T, fa func(T) A, fb func(T) B, fc func(T) C) (Mapper[A], Mapper[B], Mapper[C])

Unzip3 extracts three slices from ts in a single pass by applying the extraction functions.

func Unzip4 added in v0.6.0

func Unzip4[T, A, B, C, D any](ts []T, fa func(T) A, fb func(T) B, fc func(T) C, fd func(T) D) (Mapper[A], Mapper[B], Mapper[C], Mapper[D])

Unzip4 extracts four slices from ts in a single pass by applying the extraction functions.

Types

type Any

type Any = Mapper[any]

type Bool

type Bool = Mapper[bool]

type Byte

type Byte = Mapper[byte]

type Error

type Error = Mapper[error]

type Float32

type Float32 = Mapper[float32]

type Float64

type Float64 []float64

func (Float64) Max added in v0.23.0

func (fs Float64) Max() float64

Max returns the largest element, or zero if the slice is empty.

func (Float64) Min added in v0.23.0

func (fs Float64) Min() float64

Min returns the smallest element, or zero if the slice is empty.

func (Float64) Sum added in v0.19.0

func (fs Float64) Sum() float64

Sum returns the sum of all elements.

type Int

type Int []int

func (Int) Max added in v0.23.0

func (is Int) Max() int

Max returns the largest element, or zero if the slice is empty.

func (Int) Min added in v0.23.0

func (is Int) Min() int

Min returns the smallest element, or zero if the slice is empty.

func (Int) Sum added in v0.23.0

func (is Int) Sum() int

Sum returns the sum of all elements.

type Mapper

type Mapper[T any] []T

Mapper is a fluent slice usable anywhere a regular slice is, but provides additional fluent fp methods. Its underlying type is []T.

func From

func From[T any](ts []T) Mapper[T]

func MapAccum added in v0.25.0

func MapAccum[T, R, S any](ts []T, init S, fn func(S, T) (S, R)) (S, Mapper[R])

MapAccum threads state through a slice, producing both a final state and a mapped output. fn receives the accumulated state and current element, returning new state and an output value. Returns init and an empty slice if ts is empty.

func ParallelMap added in v0.23.0

func ParallelMap[T, R any](m Mapper[T], workers int, fn func(T) R) Mapper[R]

ParallelMap returns the result of applying fn to each member of m, using the specified number of worker goroutines. Order is preserved. The fn must be safe for concurrent use.

Example
package main

import (
	"fmt"
	"runtime"

	"github.com/binaryphile/fluentfp/slice"
)

func main() {
	double := func(n int) int { return n * 2 }
	result := slice.ParallelMap(slice.From([]int{1, 2, 3, 4, 5}), runtime.GOMAXPROCS(0), double)
	fmt.Println([]int(result))
}
Output:

[2 4 6 8 10]

func SortBy added in v0.23.0

func SortBy[T any, K cmp.Ordered](ts []T, fn func(T) K) Mapper[T]

SortBy returns a sorted copy of ts, ordered ascending by the key extracted via fn.

func SortByDesc added in v0.23.0

func SortByDesc[T any, K cmp.Ordered](ts []T, fn func(T) K) Mapper[T]

SortByDesc returns a sorted copy of ts, ordered descending by the key extracted via fn.

func (Mapper[T]) Any added in v0.23.0

func (ts Mapper[T]) Any(fn func(T) bool) bool

Any returns true if fn returns true for any element.

func (Mapper[T]) Clone added in v0.23.0

func (ts Mapper[T]) Clone() Mapper[T]

Clone returns a shallow copy of the slice with independent backing array.

func (Mapper[T]) Convert

func (ts Mapper[T]) Convert(fn func(T) T) Mapper[T]

Convert returns the result of applying fn to each member of ts.

func (Mapper[T]) Each

func (ts Mapper[T]) Each(fn func(T))

Each applies fn to each member of ts.

func (Mapper[T]) Find added in v0.17.0

func (ts Mapper[T]) Find(fn func(T) bool) option.Basic[T]

Find returns the first element matching the predicate, or not-ok if none match.

func (Mapper[T]) First added in v0.20.0

func (ts Mapper[T]) First() option.Basic[T]

First returns the first element, or not-ok if the slice is empty.

func (Mapper[T]) FlatMap added in v0.26.0

func (ts Mapper[T]) FlatMap(fn func(T) []T) Mapper[T]

FlatMap applies fn to each element, concatenating the resulting slices in iteration order. Nil slices returned by fn are treated as empty. The result is always non-nil.

func (Mapper[T]) IndexWhere added in v0.23.0

func (ts Mapper[T]) IndexWhere(fn func(T) bool) option.Basic[int]

IndexWhere returns the index of the first element matching the predicate, or not-ok if none match.

func (Mapper[T]) KeepIf

func (ts Mapper[T]) KeepIf(fn func(T) bool) Mapper[T]

KeepIf returns a new slice containing the members of ts for which fn returns true. It is the complement of RemoveIf.

func (Mapper[T]) Len

func (ts Mapper[T]) Len() int

Len returns the length of the slice.

func (Mapper[T]) ParallelEach added in v0.23.0

func (m Mapper[T]) ParallelEach(workers int, fn func(T))

ParallelEach applies fn to each member of m, using the specified number of worker goroutines. The fn must be safe for concurrent use.

Example
package main

import (
	"github.com/binaryphile/fluentfp/slice"
)

func main() {
	slice.From([]string{"a", "b", "c"}).ParallelEach(2, func(s string) {
		// process each element concurrently
		_ = s
	})
}

func (Mapper[T]) ParallelKeepIf added in v0.23.0

func (m Mapper[T]) ParallelKeepIf(workers int, fn func(T) bool) Mapper[T]

ParallelKeepIf returns a new slice containing members for which fn returns true, using the specified number of worker goroutines. Order is preserved.

Example
package main

import (
	"fmt"

	"github.com/binaryphile/fluentfp/slice"
)

func main() {
	isEven := func(n int) bool { return n%2 == 0 }
	result := slice.From([]int{1, 2, 3, 4, 5, 6}).ParallelKeepIf(4, isEven)
	fmt.Println([]int(result))
}
Output:

[2 4 6]

func (Mapper[T]) RemoveIf

func (ts Mapper[T]) RemoveIf(fn func(T) bool) Mapper[T]

RemoveIf returns a new slice containing members for which fn returns false. It is the complement of KeepIf.

func (Mapper[T]) Single added in v0.23.0

func (ts Mapper[T]) Single() either.Either[int, T]

Single returns Right(element) if exactly one element exists, or Left(count) if zero or more than one.

func (Mapper[T]) TakeFirst

func (ts Mapper[T]) TakeFirst(n int) Mapper[T]

TakeFirst returns the first n elements of ts.

func (Mapper[T]) ToAny

func (ts Mapper[T]) ToAny(fn func(T) any) Mapper[any]

ToAny returns the result of applying fn to each member of ts.

func (Mapper[T]) ToBool

func (ts Mapper[T]) ToBool(fn func(T) bool) Mapper[bool]

ToBool returns the result of applying fn to each member of ts.

func (Mapper[T]) ToByte

func (ts Mapper[T]) ToByte(fn func(T) byte) Mapper[byte]

ToByte returns the result of applying fn to each member of ts.

func (Mapper[T]) ToError

func (ts Mapper[T]) ToError(fn func(T) error) Mapper[error]

ToError returns the result of applying fn to each member of ts.

func (Mapper[T]) ToFloat32

func (ts Mapper[T]) ToFloat32(fn func(T) float32) Mapper[float32]

ToFloat32 returns the result of applying fn to each member of ts.

func (Mapper[T]) ToFloat64

func (ts Mapper[T]) ToFloat64(fn func(T) float64) Float64

ToFloat64 returns the result of applying fn to each member of ts.

func (Mapper[T]) ToInt

func (ts Mapper[T]) ToInt(fn func(T) int) Int

ToInt returns the result of applying fn to each member of ts.

func (Mapper[T]) ToInt32 added in v0.8.0

func (ts Mapper[T]) ToInt32(fn func(T) int32) Mapper[int32]

ToInt32 returns the result of applying fn to each member of ts.

func (Mapper[T]) ToInt64 added in v0.8.0

func (ts Mapper[T]) ToInt64(fn func(T) int64) Mapper[int64]

ToInt64 returns the result of applying fn to each member of ts.

func (Mapper[T]) ToRune

func (ts Mapper[T]) ToRune(fn func(T) rune) Mapper[rune]

ToRune returns the result of applying fn to each member of ts.

func (Mapper[T]) ToString

func (ts Mapper[T]) ToString(fn func(T) string) String

ToString returns the result of applying fn to each member of ts.

type MapperTo

type MapperTo[R, T any] []T

MapperTo is a fluent slice with one additional method, MapTo, for mapping to a specified type R. If you don't need to map to an arbitrary type, use Mapper instead.

func MapTo

func MapTo[R, T any](ts []T) MapperTo[R, T]

func (MapperTo[R, T]) Clone added in v0.23.0

func (ts MapperTo[R, T]) Clone() MapperTo[R, T]

Clone returns a shallow copy of the slice with independent backing array.

func (MapperTo[R, T]) Convert

func (ts MapperTo[R, T]) Convert(fn func(T) T) MapperTo[R, T]

Convert returns the result of applying fn to each member of ts.

func (MapperTo[R, T]) Each

func (ts MapperTo[R, T]) Each(fn func(T))

Each applies fn to each member of ts.

func (MapperTo[R, T]) First added in v0.20.0

func (ts MapperTo[R, T]) First() option.Basic[T]

First returns the first element, or not-ok if the slice is empty.

func (MapperTo[R, T]) FlatMap added in v0.26.0

func (ts MapperTo[R, T]) FlatMap(fn func(T) []R) Mapper[R]

FlatMap applies fn to each element, concatenating the resulting slices in iteration order. Nil slices returned by fn are treated as empty. The result is always non-nil.

func (MapperTo[R, T]) KeepIf

func (ts MapperTo[R, T]) KeepIf(fn func(T) bool) MapperTo[R, T]

KeepIf returns a new slice containing the members of ts for which fn returns true. It is the complement of RemoveIf.

func (MapperTo[R, T]) Len

func (ts MapperTo[R, T]) Len() int

Len returns the length of the slice.

func (MapperTo[R, T]) Map added in v0.12.0

func (ts MapperTo[R, T]) Map(fn func(T) R) Mapper[R]

Map returns the result of applying fn to each member of ts.

func (MapperTo[R, T]) ParallelEach added in v0.23.0

func (ts MapperTo[R, T]) ParallelEach(workers int, fn func(T))

ParallelEach applies fn to each member of ts, using the specified number of worker goroutines. The fn must be safe for concurrent use.

func (MapperTo[R, T]) ParallelKeepIf added in v0.23.0

func (ts MapperTo[R, T]) ParallelKeepIf(workers int, fn func(T) bool) MapperTo[R, T]

ParallelKeepIf returns a new slice containing members for which fn returns true, using the specified number of worker goroutines. Order is preserved.

func (MapperTo[R, T]) ParallelMap added in v0.23.0

func (ts MapperTo[R, T]) ParallelMap(workers int, fn func(T) R) Mapper[R]

ParallelMap returns the result of applying fn to each member of ts, using the specified number of worker goroutines. Order is preserved. The fn must be safe for concurrent use.

func (MapperTo[R, T]) RemoveIf

func (ts MapperTo[R, T]) RemoveIf(fn func(T) bool) MapperTo[R, T]

RemoveIf returns a new slice containing members for which fn returns false. It is the complement of KeepIf.

func (MapperTo[R, T]) Single added in v0.23.0

func (ts MapperTo[R, T]) Single() either.Either[int, T]

Single returns Right(element) if exactly one element exists, or Left(count) if zero or more than one.

func (MapperTo[R, T]) TakeFirst

func (ts MapperTo[R, T]) TakeFirst(n int) MapperTo[R, T]

TakeFirst returns the first n members of ts.

func (MapperTo[R, T]) ToAny

func (ts MapperTo[R, T]) ToAny(fn func(T) any) MapperTo[R, any]

ToAny returns the result of applying fn to each member of ts.

func (MapperTo[R, T]) ToBool

func (ts MapperTo[R, T]) ToBool(fn func(T) bool) MapperTo[R, bool]

ToBool returns the result of applying fn to each member of ts.

func (MapperTo[R, T]) ToByte

func (ts MapperTo[R, T]) ToByte(fn func(T) byte) MapperTo[R, byte]

ToByte returns the result of applying fn to each member of ts.

func (MapperTo[R, T]) ToError

func (ts MapperTo[R, T]) ToError(fn func(T) error) MapperTo[R, error]

ToError returns the result of applying fn to each member of ts.

func (MapperTo[R, T]) ToFloat32

func (ts MapperTo[R, T]) ToFloat32(fn func(T) float32) MapperTo[R, float32]

ToFloat32 returns the result of applying fn to each member of ts.

func (MapperTo[R, T]) ToFloat64

func (ts MapperTo[R, T]) ToFloat64(fn func(T) float64) MapperTo[R, float64]

ToFloat64 returns the result of applying fn to each member of ts.

func (MapperTo[R, T]) ToInt

func (ts MapperTo[R, T]) ToInt(fn func(T) int) MapperTo[R, int]

ToInt returns the result of applying fn to each member of ts.

func (MapperTo[R, T]) ToInt32 added in v0.8.0

func (ts MapperTo[R, T]) ToInt32(fn func(T) int32) MapperTo[R, int32]

ToInt32 returns the result of applying fn to each member of ts.

func (MapperTo[R, T]) ToInt64 added in v0.8.0

func (ts MapperTo[R, T]) ToInt64(fn func(T) int64) MapperTo[R, int64]

ToInt64 returns the result of applying fn to each member of ts.

func (MapperTo[R, T]) ToRune

func (ts MapperTo[R, T]) ToRune(fn func(T) rune) MapperTo[R, rune]

ToRune returns the result of applying fn to each member of ts.

func (MapperTo[R, T]) ToString

func (ts MapperTo[R, T]) ToString(fn func(T) string) MapperTo[R, string]

ToString returns the result of applying fn to each member of ts.

type Rune

type Rune = Mapper[rune]

type String

type String []string

func (String) Contains added in v0.23.0

func (ss String) Contains(target string) bool

Contains returns true if ss contains target.

func (String) ContainsAny added in v0.23.0

func (ss String) ContainsAny(targets []string) bool

ContainsAny returns true if ss contains any element in targets. Returns false if either slice is empty.

func (String) Each added in v0.23.0

func (ss String) Each(fn func(string))

Each calls fn for every element.

func (String) Len added in v0.23.0

func (ss String) Len() int

Len returns the length of the slice.

func (String) Matches added in v0.23.0

func (ss String) Matches(filter []string) bool

Matches returns true if ss contains any element in filter. Returns true if filter is empty (no constraint).

func (String) ToSet added in v0.23.0

func (ss String) ToSet() map[string]bool

ToSet returns a map with each string as a key set to true.

func (String) Unique added in v0.23.0

func (ss String) Unique() String

Unique returns a new slice with duplicate strings removed, preserving order.

Jump to

Keyboard shortcuts

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