{"id":35432690,"url":"https://github.com/goforj/collection","last_synced_at":"2026-01-13T19:57:31.458Z","repository":{"id":328721528,"uuid":"1111966779","full_name":"goforj/collection","owner":"goforj","description":"A fluent, Laravel-inspired Collection library for Go - with chaining, higher-order functions, and expressive data manipulation.","archived":false,"fork":false,"pushed_at":"2026-01-04T08:57:38.000Z","size":2434,"stargazers_count":28,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-07T10:56:06.471Z","etag":null,"topics":["collections","data-structures","developer-experience","dx","generics","go","golang","laravel-inspired","map-utils","slice-utils"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/goforj.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-08T00:34:44.000Z","updated_at":"2026-01-04T20:26:47.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/goforj/collection","commit_stats":null,"previous_names":["goforj/collection"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/goforj/collection","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goforj%2Fcollection","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goforj%2Fcollection/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goforj%2Fcollection/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goforj%2Fcollection/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/goforj","download_url":"https://codeload.github.com/goforj/collection/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goforj%2Fcollection/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28398492,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-13T14:36:09.778Z","status":"ssl_error","status_checked_at":"2026-01-13T14:35:19.697Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["collections","data-structures","developer-experience","dx","generics","go","golang","laravel-inspired","map-utils","slice-utils"],"created_at":"2026-01-02T21:17:44.740Z","updated_at":"2026-01-13T19:57:31.451Z","avatar_url":"https://github.com/goforj.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./docs/assets/logo.png\" width=\"400\" alt=\"goforj/collection logo\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    Fluent collections for Go - with generics, chainable pipelines, and expressive data transforms. Inspired by Laravel, designed to feel natural in Go.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://pkg.go.dev/github.com/goforj/collection\"\u003e\u003cimg src=\"https://pkg.go.dev/badge/github.com/goforj/collection.svg\" alt=\"Go Reference\"\u003e\u003c/a\u003e\n    \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-MIT-blue.svg\" alt=\"License: MIT\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/goforj/collection/actions\"\u003e\u003cimg src=\"https://github.com/goforj/collection/actions/workflows/test.yml/badge.svg\" alt=\"Go Test\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://golang.org\"\u003e\u003cimg src=\"https://img.shields.io/badge/go-1.21+-blue?logo=go\" alt=\"Go version\"\u003e\u003c/a\u003e\n    \u003cimg src=\"https://img.shields.io/github/v/tag/goforj/collection?label=version\u0026sort=semver\" alt=\"Latest tag\"\u003e\n    \u003ca href=\"https://codecov.io/gh/goforj/collection\" \u003e\u003cimg src=\"https://codecov.io/github/goforj/collection/graph/badge.svg?token=3KFTK96U8C\"/\u003e\u003c/a\u003e\n\u003c!-- test-count:embed:start --\u003e\n    \u003cimg src=\"https://img.shields.io/badge/tests-470-brightgreen\" alt=\"Tests\"\u003e\n\u003c!-- test-count:embed:end --\u003e\n    \u003ca href=\"https://goreportcard.com/report/github.com/goforj/collection\"\u003e\u003cimg src=\"https://goreportcard.com/badge/github.com/goforj/collection\" alt=\"Go Report Card\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ccode\u003ecollection\u003c/code\u003e brings an expressive, fluent API to Go.\n    Iterate, filter, transform, sort, reduce, group, and debug data with zero dependencies - familiar to Go developers, pleasant to use everywhere.\n\u003c/p\u003e\n\n## Features\n\n- **Fluent chaining** - pipeline your operations like Laravel Collections\n- **Fully generic** (`Collection[T]`) - no reflection, no `interface{}`\n- **Minimal dependencies** - small footprint (godump for debugging)\n- **Minimal allocations** - slice views where possible; in-place ops reuse backing storage when semantics allow\n- **Map / Filter / Reduce** - clean functional transforms\n- **First / Last / FirstWhere / IndexWhere / Contains** helpers\n- **Sort, GroupBy, Chunk**, and more\n- **Borrow-by-default** - no defensive copies unless you ask for them\n- **Built-in JSON helpers** (`ToJSON()`, `ToPrettyJSON()`)\n- **Developer-friendly debug helpers** (`Dump()`, `Dd()`, `DumpStr()`)\n- **Works with any Go type**, including structs, pointers, and deeply nested composites\n\n## Fluent Chaining\n\nMany methods return the collection itself, allowing for fluent method chaining.\n\nSome methods may be limited due to Go's generic constraints.\n\n\u003e **Fluent example:**  \n\u003e [`examples/chaining/main.go`](./examples/chaining/main.go)\n\n```go\nevents := []DeviceEvent{\n    {Device: \"router-1\", Region: \"us-east\", Errors: 3},\n    {Device: \"router-2\", Region: \"us-east\", Errors: 15},\n    {Device: \"router-3\", Region: \"us-west\", Errors: 22},\n}\n\n// Fluent slice pipeline\ncollection.\n    New(events). // Construction\n    Shuffle(). // Ordering\n    Filter(func(e DeviceEvent) bool { return e.Errors \u003e 5 }). // Slicing\n    Sort(func(a, b DeviceEvent) bool { return a.Errors \u003e b.Errors }). // Ordering\n    Take(5). // Slicing\n    TakeUntilFn(func(e DeviceEvent) bool { return e.Errors \u003c 10 }). // Slicing (stop when predicate becomes true)\n    SkipLast(1). // Slicing\n    Dump() // Debugging\n\n// #[]main.DeviceEvent [\n//  0 =\u003e #main.DeviceEvent {\n//    +Device =\u003e \"router-3\" #string\n//    +Region =\u003e \"us-west\" #string\n//    +Errors =\u003e 22 #int\n//  }\n// ]\n```\n\n### Performance Benchmarks\n\n\u003e **tl;dr**: *lo* is excellent. We solve a different problem - and in chained pipelines, that difference matters.\n\n`lo` is a fantastic library and a major inspiration for this project. It is battle-tested, idiomatic, and often the right choice when you want small, standalone helpers that operate on slices in isolation.\n\n`collection` takes a different approach.\n\nRather than treating each operation as an independent transformation, `collection` is built around **explicit, fluent pipelines**. Many operations are designed to **mutate the same backing slice intentionally**, allowing chained workflows to avoid intermediate allocations and unnecessary copying - while still making that behavior visible and documented.\n\nThat design choice doesn't matter much for some single operations. It matters a *lot* once you start chaining and especially in hot paths.\n\nBelow - A fixed ~24B allocation is the cost of the Collection wrapper (one-time per pipeline), not additional work per operation\n\nThe below tables are automatically generated from [`./docs/bench/main.go`](./docs/bench/main.go).\n\n\u003c!-- bench:embed:start --\u003e\n\nFull raw tables: see `BENCHMARKS.md`.\n\n#### Read-only scalar ops (wrapper overhead only)\n\n| Op | Speed vs lo | Memory | Allocs |\n|---:|:-----------:|:------:|:------:|\n| **All** | ≈ | +24B | +1 |\n| **Any** | ≈ | +24B | +1 |\n| **None** | ≈ | +24B | +1 |\n| **First** | ≈ | +24B | +1 |\n| **Last** | ≈ | +24B | +1 |\n| **FirstWhere** | ≈ | +24B | +1 |\n| **IndexWhere** | ≈ | +24B | +1 |\n| **Contains** | ≈ | +24B | +1 |\n| **Reduce (sum)** | ≈ | +24B | +1 |\n| **Sum** | ≈ | +32B | +2 |\n| **Min** | ≈ | +32B | +2 |\n| **Max** | ≈ | +32B | +2 |\n| **Each** | ≈ | +24B | +1 |\n\n#### Transforming ops\n\n| Op | Speed vs lo | Memory | Allocs |\n|---:|:-----------:|:------:|:------:|\n| **Chunk** | **7.64x** | -8.0KB | -49 |\n| **Take** | ≈ | +48B | +2 |\n| **Skip** | **71.40x** | -8.2KB | ≈ |\n| **SkipLast** | **71.90x** | -8.2KB | ≈ |\n| **Zip** | **2.35x** | +48B | +2 |\n| **ZipWith** | **3.15x** | +48B | +2 |\n| **Unique** | ≈ | +24B | +1 |\n| **UniqueBy** | ≈ | +48B | +2 |\n| **Union** | ≈ | +72B | +3 |\n| **Intersect** | ≈ | +72B | +3 |\n| **Difference** | **2.33x** | -26.7KB | -29 |\n| **GroupBySlice** | ≈ | +24B | +1 |\n| **CountBy** | ≈ | +24B | +1 |\n| **CountByValue** | ≈ | +24B | +1 |\n| **ToMap** | ≈ | -24B | ≈ |\n\n#### Pipelines\n\n| Op | Speed vs lo | Memory | Allocs |\n|---:|:-----------:|:------:|:------:|\n| **Pipeline F→M→T→R** | **1.79x** | -12.2KB | ≈ |\n\n#### Mutating ops\n\n| Op | Speed vs lo | Memory | Allocs |\n|---:|:-----------:|:------:|:------:|\n| **Map** | **2.21x** | -8.2KB | ≈ |\n| **Filter** | **1.41x** | -8.2KB | ≈ |\n| **Reverse** | ≈ | +24B | +1 |\n| **Shuffle** | **1.57x** | +24B | +1 |\n\u003c!-- bench:embed:end --\u003e\n\n## How to read the benchmarks\n\n* **≈** means the two libraries are effectively equivalent\n* Explicit memory deltas show fixed wrapper overhead vs avoided allocations\n* Single-operation helpers are intentionally close in performance if not exceeds\n* Multi-step pipelines highlight the architectural difference\n\nIf you prefer immutable, one-off helpers - `lo` is outstanding.\nIf you write **expressive, chained data pipelines** and care about hot-path performance - `collection` is built for that job.\n\n\n## Why chaining changes the performance story\n\nMost functional helpers (including `lo`) operate like this:\n\n```\ninput → Map → new slice → Filter → new slice → Take → new slice\n```\n\nThat model is simple and safe - but each step typically allocates.\n\n`collection` pipelines are designed to look more like this:\n\n```\ninput → Filter (in place) → Map (in place) → Take (slice view)\n```\n\nWhen you opt into mutation, **the pipeline stays on the same backing array** unless an operation explicitly documents that it allocates. The result is:\n\n* **Fewer allocations**\n* **Less GC pressure**\n* **Lower end-to-end latency in hot paths**\n* **Much stronger scaling in multi-step pipelines**\n\nThat's why the biggest deltas appear in benchmarks like:\n\n* `Pipeline F→M→T→R`\n* `Map`\n* `Filter`\n* `Chunk`\n* `Zip / ZipWith`\n* `Skip / SkipLast`\n\nIn these cases, `collection` can be **2×–30× faster** and often reduce allocations to **zero**, not by doing \"clever tricks\", but by making mutation *explicit and opt-in*.\n\n## Explicit branching with `Clone`\n\nFluent pipelines don't mean you're locked into mutation.\n\nThis library borrows slices by default. It does not perform defensive copies.\nUse `Clone()` or `ItemsCopy()` to explicitly copy.\n\nWhen you want to branch a pipeline or preserve the original data, `Clone()` creates a shallow copy of the collection so subsequent operations are isolated and predictable.\n\n```go\nevents := collection.New(deviceEvents)\n\n// Fast alerting path: cheap filters, early exit\nalerts := events.\n    Clone().\n    Filter(func(e DeviceEvent) bool { return e.Severity \u003e= Critical }).\n    Take(10)\n\n// Deeper analysis path: heavier work, full ordering\nreport := events.\n    Clone().\n    Filter(func(e DeviceEvent) bool { return e.Region == \"us-east\" }).\n    Sort(func(a, b DeviceEvent) bool { return a.Timestamp.Before(b.Timestamp) })\n```\n\nThis keeps the performance benefits of in-place operations **where they matter**, while making divergence points explicit and intentional.\n\nNo hidden copies. No surprises.\n\n## Design Principles\n\n- **Type-safe**: no reflection\n- **Explicit semantics**: order, mutation, and allocation are documented\n- **Go-native**: respects generics and stdlib patterns\n- **Eager evaluation**: no lazy pipelines or hidden concurrency\n- **Maps are boundaries**: unordered data is handled explicitly\n\n## What this library is not\n\n- Not a lazy or streaming library\n- Not concurrency-aware\n- Not immutable-by-default\n- Not a replacement for idiomatic loops in simple cases\n- Not designed to hide allocation, mutation, or ordering semantics\n\n## Working with maps\n\nMaps are unordered in Go. This library does not pretend otherwise.\n\nInstead, map interaction is explicit and intentional:\n\n- `FromMap` materializes key/value pairs into an ordered workflow\n- `ToMap` reduces collections back into maps explicitly\n- `ToMapKV` provides a convenience for `Pair[K,V]`\n\nThis makes transitions between unordered and ordered data visible and honest.\n\n## Behavior semantics\n\nEach method declares how it interacts with the collection:\n\n- **readonly** - reads data only and returns a derived value\n- **immutable** - returns a new collection; the original is unchanged\n- **mutable** - modifies the collection in place and returns the same instance\n- **terminal** - ends the fluent pipeline and returns a non-collection result\n\nThese annotations describe **observable behavior**, not implementation details.\n\nTerminal operations do not return a Collection and cannot be chained further.\nThey are designed to be allocation-free under `New()` where possible.\n\nAllocation and copying are **explicitly documented per method**.\nSome readonly or immutable operations may allocate internally when required\n(e.g. grouping, chunking, scratch copies), but never mutate the receiver.\n\nBorrowed slices, in-place mutation, and view semantics are intentional and visible.\n\n## Runnable examples\n\nEvery function has a corresponding runnable example under [`./examples`](./examples).\n\nThese examples are **generated directly from the documentation blocks** of each function, ensuring the docs and code never drift. These are the same examples you see here in the README and GoDoc.\n\nAn automated test executes **every example** to verify it builds and runs successfully.  \n\nThis guarantees all examples are valid, up-to-date, and remain functional as the API evolves.\n\n# Installation\n\n```bash\ngo get github.com/goforj/collection\n```\n\n\u003c!-- api:embed:start --\u003e\n\n# API Index\n\n| Group | Functions |\n|------:|-----------|\n| **Access** | [Items](#items) [ItemsCopy](#itemscopy) |\n| **Aggregation** | [Avg](#avg) [Count](#count) [CountBy](#countby) [CountByValue](#countbyvalue) [Max](#max) [MaxBy](#maxby) [Median](#median) [Min](#min) [MinBy](#minby) [Mode](#mode) [Reduce](#reduce) [Sum](#sum) |\n| **Construction** | [Clone](#clone) [New](#new) [NewNumeric](#newnumeric) |\n| **Debugging** | [Dd](#dd) [Dump](#dump) [DumpStr](#dumpstr) |\n| **Grouping** | [GroupBy](#groupby) [GroupBySlice](#groupbyslice) |\n| **Maps** | [FromMap](#frommap) [ToMap](#tomap) [ToMapKV](#tomapkv) |\n| **Ordering** | [After](#after) [Before](#before) [Reverse](#reverse) [Shuffle](#shuffle) [Sort](#sort) |\n| **Querying** | [All](#all) [Any](#any) [At](#at) [Contains](#contains) [First](#first) [FirstWhere](#firstwhere) [IndexWhere](#indexwhere) [IsEmpty](#isempty) [Last](#last) [LastWhere](#lastwhere) [None](#none) |\n| **Serialization** | [ToJSON](#tojson) [ToPrettyJSON](#toprettyjson) |\n| **Set Operations** | [Difference](#difference) [Intersect](#intersect) [SymmetricDifference](#symmetricdifference) [Union](#union) [Unique](#unique) [UniqueBy](#uniqueby) [UniqueComparable](#uniquecomparable) |\n| **Slicing** | [Chunk](#chunk) [Filter](#filter) [Partition](#partition) [Pop](#pop) [PopN](#popn) [Skip](#skip) [SkipLast](#skiplast) [Take](#take) [TakeLast](#takelast) [TakeUntil](#takeuntil) [TakeUntilFn](#takeuntilfn) [Window](#window) |\n| **Transformation** | [Append](#append) [Concat](#concat) [Each](#each) [Map](#map) [MapTo](#mapto) [Merge](#merge) [Multiply](#multiply) [Pipe](#pipe) [Prepend](#prepend) [Tap](#tap) [Times](#times) [Transform](#transform) [Zip](#zip) [ZipWith](#zipwith) |\n\n\n## Access\n\n### \u003ca id=\"items\"\u003e\u003c/a\u003eItems · readonly · terminal\n\nItems returns the backing slice of items.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{1, 2, 3})\nitems := c.Items()\ncollection.Dump(items)\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 3 #int\n// ]\n```\n\n_Example: strings_\n\n```go\nc2 := collection.New([]string{\"apple\", \"banana\"})\nitems2 := c2.Items()\ncollection.Dump(items2)\n// #[]string [\n//   0 =\u003e \"apple\" #string\n//   1 =\u003e \"banana\" #string\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n})\n\nout := users.Items()\ncollection.Dump(out)\n// #[]main.User [\n//   0 =\u003e #main.User {\n//     +ID   =\u003e 1 #int\n//     +Name =\u003e \"Alice\" #string\n//   }\n//   1 =\u003e #main.User {\n//     +ID   =\u003e 2 #int\n//     +Name =\u003e \"Bob\" #string\n//   }\n// ]\n```\n\n### \u003ca id=\"itemscopy\"\u003e\u003c/a\u003eItemsCopy · readonly · terminal\n\nItemsCopy returns a copy of the collection's items.\n\n```go\nc := collection.New([]int{1, 2, 3})\nitems := c.ItemsCopy()\ncollection.Dump(items)\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 3 #int\n// ]\n```\n\n## Aggregation\n\n### \u003ca id=\"avg\"\u003e\u003c/a\u003eAvg · readonly · terminal\n\nAvg returns the average of the collection values as a float64.\nIf the collection is empty, Avg returns 0.\n\n_Example: integers_\n\n```go\nc := collection.NewNumeric([]int{2, 4, 6})\ncollection.Dump(c.Avg())\n// 4.000000 #float64\n```\n\n_Example: float_\n\n```go\nc2 := collection.NewNumeric([]float64{1.5, 2.5, 3.0})\ncollection.Dump(c2.Avg())\n// 2.333333 #float64\n```\n\n### \u003ca id=\"count\"\u003e\u003c/a\u003eCount · readonly · terminal\n\nCount returns the total number of items in the collection.\n\n```go\ncount := collection.New([]int{1, 2, 3, 4}).Count()\ncollection.Dump(count)\n// 4 #int\n```\n\n### \u003ca id=\"countby\"\u003e\u003c/a\u003eCountBy · readonly · terminal\n\nCountBy returns a map of keys extracted by fn to their occurrence counts.\nK must be comparable.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{1, 2, 2, 3, 3, 3})\ncounts := collection.CountBy(c, func(v int) int {\n\treturn v\n})\ncollection.Dump(counts)\n// #map[int]int {\n//   1 =\u003e 1 #int\n//   2 =\u003e 2 #int\n//   3 =\u003e 3 #int\n// }\n```\n\n_Example: strings_\n\n```go\nc2 := collection.New([]string{\"apple\", \"banana\", \"apple\", \"cherry\", \"banana\"})\ncounts2 := collection.CountBy(c2, func(v string) string {\n\treturn v\n})\ncollection.Dump(counts2)\n// #map[string]int {\n//   apple =\u003e 2 #int\n//   banana =\u003e 2 #int\n//   cherry =\u003e 1 #int\n// }\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tName string\n\tRole string\n}\n\nusers := collection.New([]User{\n\t{Name: \"Alice\", Role: \"admin\"},\n\t{Name: \"Bob\", Role: \"user\"},\n\t{Name: \"Carol\", Role: \"admin\"},\n\t{Name: \"Dave\", Role: \"user\"},\n\t{Name: \"Eve\", Role: \"admin\"},\n})\n\nroleCounts := collection.CountBy(users, func(u User) string {\n\treturn u.Role\n})\n\ncollection.Dump(roleCounts)\n// #map[string]int {\n//   admin =\u003e 3 #int\n//   user =\u003e 2 #int\n// }\n```\n\n### \u003ca id=\"countbyvalue\"\u003e\u003c/a\u003eCountByValue · readonly · terminal\n\nCountByValue returns a map where each distinct item in the collection\nis mapped to the number of times it appears.\n\n_Example: strings_\n\n```go\nc1 := collection.New([]string{\"a\", \"b\", \"a\"})\ncounts1 := collection.CountByValue(c1)\ncollection.Dump(counts1)\n// #map[string]int {\n//  a =\u003e 2 #int\n//  b =\u003e 1 #int\n// }\n```\n\n_Example: integers_\n\n```go\nc2 := collection.New([]int{1, 2, 2, 3, 3, 3})\ncounts2 := collection.CountByValue(c2)\ncollection.Dump(counts2)\n// #map[int]int {\n//  1 =\u003e 1 #int\n//  2 =\u003e 2 #int\n//  3 =\u003e 3 #int\n// }\n```\n\n_Example: structs (comparable)_\n\n```go\ntype Point struct {\n\tX int\n\tY int\n}\n\nc3 := collection.New([]Point{\n\t{X: 1, Y: 1},\n\t{X: 2, Y: 2},\n\t{X: 1, Y: 1},\n})\n\ncounts3 := collection.CountByValue(c3)\ncollection.Dump(counts3)\n// #map[main.Point]int {\n//  {1 1} =\u003e 2 #int\n//  {2 2} =\u003e 1 #int\n// }\n```\n\n### \u003ca id=\"max\"\u003e\u003c/a\u003eMax · readonly · terminal\n\nMax returns the largest numeric item in the collection.\nThe second return value is false if the collection is empty.\n\n_Example: integers_\n\n```go\nc := collection.NewNumeric([]int{3, 1, 2})\n\nmax1, ok1 := c.Max()\ncollection.Dump(max1, ok1)\n// 3 #int\n// true #bool\n```\n\n_Example: floats_\n\n```go\nc2 := collection.NewNumeric([]float64{1.5, 9.2, 4.4})\n\nmax2, ok2 := c2.Max()\ncollection.Dump(max2, ok2)\n// 9.200000 #float64\n// true #bool\n```\n\n_Example: empty numeric collection_\n\n```go\nc3 := collection.NewNumeric([]int{})\n\nmax3, ok3 := c3.Max()\ncollection.Dump(max3, ok3)\n// 0 #int\n// false #bool\n```\n\n### \u003ca id=\"maxby\"\u003e\u003c/a\u003eMaxBy · readonly · terminal\n\nMaxBy returns the item whose key (produced by keyFn) is the largest.\nThe second return value is false if the collection is empty.\n\n_Example: structs - highest score_\n\n```go\ntype Player struct {\n\tName  string\n\tScore int\n}\n\nplayers := collection.New([]Player{\n\t{Name: \"Alice\", Score: 10},\n\t{Name: \"Bob\", Score: 25},\n\t{Name: \"Carol\", Score: 18},\n})\n\ntop, ok := collection.MaxBy(players, func(p Player) int {\n\treturn p.Score\n})\n\ncollection.Dump(top, ok)\n// #main.Player {\n//   +Name  =\u003e \"Bob\" #string\n//   +Score =\u003e 25 #int\n// }\n// true #bool\n```\n\n_Example: strings - longest length_\n\n```go\nwords := collection.New([]string{\"go\", \"collection\", \"rocks\"})\n\nlongest, ok := collection.MaxBy(words, func(s string) int {\n\treturn len(s)\n})\n\ncollection.Dump(longest, ok)\n// \"collection\" #string\n// true #bool\n```\n\n_Example: empty collection_\n\n```go\nempty := collection.New([]int{})\nmaxVal, ok := collection.MaxBy(empty, func(v int) int { return v })\ncollection.Dump(maxVal, ok)\n// 0 #int\n// false #bool\n```\n\n### \u003ca id=\"median\"\u003e\u003c/a\u003eMedian · readonly · terminal\n\nMedian returns the statistical median of the numeric collection as float64.\nReturns (0, false) if the collection is empty.\n\n_Example: integers - odd number of items_\n\n```go\nc := collection.NewNumeric([]int{3, 1, 2})\n\nmedian1, ok1 := c.Median()\ncollection.Dump(median1, ok1)\n// 2.000000 #float64\n// true #bool\n```\n\n_Example: integers - even number of items_\n\n```go\nc2 := collection.NewNumeric([]int{10, 2, 4, 6})\n\nmedian2, ok2 := c2.Median()\ncollection.Dump(median2, ok2)\n// 5.000000 #float64\n// true #bool\n```\n\n_Example: floats_\n\n```go\nc3 := collection.NewNumeric([]float64{1.1, 9.9, 3.3})\n\nmedian3, ok3 := c3.Median()\ncollection.Dump(median3, ok3)\n// 3.300000 #float64\n// true #bool\n```\n\n_Example: integers - empty numeric collection_\n\n```go\nc4 := collection.NewNumeric([]int{})\n\nmedian4, ok4 := c4.Median()\ncollection.Dump(median4, ok4)\n// 0.000000 #float64\n// false #bool\n```\n\n### \u003ca id=\"min\"\u003e\u003c/a\u003eMin · readonly · terminal\n\nMin returns the smallest numeric item in the collection.\nThe second return value is false if the collection is empty.\n\n_Example: integers_\n\n```go\nc := collection.NewNumeric([]int{3, 1, 2})\nmin, ok := c.Min()\ncollection.Dump(min, ok)\n// 1 #int\n// true #bool\n```\n\n_Example: floats_\n\n```go\nc2 := collection.NewNumeric([]float64{2.5, 9.1, 1.2})\nmin2, ok2 := c2.Min()\ncollection.Dump(min2, ok2)\n// 1.200000 #float64\n// true #bool\n```\n\n_Example: integers - empty collection_\n\n```go\nempty := collection.NewNumeric([]int{})\nmin3, ok3 := empty.Min()\ncollection.Dump(min3, ok3)\n// 0 #int\n// false #bool\n```\n\n### \u003ca id=\"minby\"\u003e\u003c/a\u003eMinBy · readonly · terminal\n\nMinBy returns the item whose key (produced by keyFn) is the smallest.\nThe second return value is false if the collection is empty.\n\n_Example: structs - smallest age_\n\n```go\ntype User struct {\n\tName string\n\tAge  int\n}\n\nusers := collection.New([]User{\n\t{Name: \"Alice\", Age: 30},\n\t{Name: \"Bob\", Age: 25},\n\t{Name: \"Carol\", Age: 40},\n})\n\nminUser, ok := collection.MinBy(users, func(u User) int {\n\treturn u.Age\n})\n\ncollection.Dump(minUser, ok)\n// #main.User {\n//   +Name =\u003e \"Bob\" #string\n//   +Age  =\u003e 25 #int\n// }\n// true #bool\n```\n\n_Example: strings - shortest length_\n\n```go\nwords := collection.New([]string{\"apple\", \"fig\", \"banana\"})\n\nshortest, ok := collection.MinBy(words, func(s string) int {\n\treturn len(s)\n})\n\ncollection.Dump(shortest, ok)\n// \"fig\" #string\n// true #bool\n```\n\n_Example: empty collection_\n\n```go\nempty := collection.New([]int{})\nminVal, ok := collection.MinBy(empty, func(v int) int { return v })\ncollection.Dump(minVal, ok)\n// 0 #int\n// false #bool\n```\n\n### \u003ca id=\"mode\"\u003e\u003c/a\u003eMode · readonly · terminal\n\nMode returns the most frequent numeric value(s) in the collection.\nIf multiple values tie for highest frequency, all are returned\nin first-seen order.\n\n_Example: integers – single mode_\n\n```go\nc := collection.NewNumeric([]int{1, 2, 2, 3})\nmode := c.Mode()\ncollection.Dump(mode)\n// #[]int [\n//   0 =\u003e 2 #int\n// ]\n```\n\n_Example: integers – tie for mode_\n\n```go\nc2 := collection.NewNumeric([]int{1, 2, 1, 2})\nmode2 := c2.Mode()\ncollection.Dump(mode2)\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n// ]\n```\n\n_Example: floats_\n\n```go\nc3 := collection.NewNumeric([]float64{1.1, 2.2, 1.1, 3.3})\nmode3 := c3.Mode()\ncollection.Dump(mode3)\n// #[]float64 [\n//   0 =\u003e 1.100000 #float64\n// ]\n```\n\n_Example: integers - empty collection_\n\n```go\nempty := collection.NewNumeric([]int{})\nmode4 := empty.Mode()\ncollection.Dump(mode4)\n// []int(nil)\n```\n\n### \u003ca id=\"reduce\"\u003e\u003c/a\u003eReduce · readonly · terminal\n\nReduce collapses the collection into a single accumulated value.\nThe accumulator has the same type T as the collection's elements.\n\n_Example: integers - sum_\n\n```go\nsum := collection.New([]int{1, 2, 3}).Reduce(0, func(acc, n int) int {\n\treturn acc + n\n})\ncollection.Dump(sum)\n// 6 #int\n```\n\n_Example: strings_\n\n```go\njoined := collection.New([]string{\"a\", \"b\", \"c\"}).Reduce(\"\", func(acc, s string) string {\n\treturn acc + s\n})\ncollection.Dump(joined)\n// \"abc\" #string\n```\n\n_Example: structs_\n\n```go\ntype Stats struct {\n\tCount int\n\tSum   int\n}\n\nstats := collection.New([]Stats{\n\t{Count: 1, Sum: 10},\n\t{Count: 1, Sum: 20},\n\t{Count: 1, Sum: 30},\n})\n\ntotal := stats.Reduce(Stats{}, func(acc, s Stats) Stats {\n\tacc.Count += s.Count\n\tacc.Sum += s.Sum\n\treturn acc\n})\n\ncollection.Dump(total)\n// #main.Stats {\n//   +Count =\u003e 3 #int\n//   +Sum   =\u003e 60 #int\n// }\n```\n\n### \u003ca id=\"sum\"\u003e\u003c/a\u003eSum · readonly · terminal\n\nSum returns the sum of all numeric items in the NumericCollection.\nIf the collection is empty, Sum returns the zero value of T.\n\n_Example: integers_\n\n```go\nc := collection.NewNumeric([]int{1, 2, 3})\ntotal := c.Sum()\ncollection.Dump(total)\n// 6 #int\n```\n\n_Example: floats_\n\n```go\nc2 := collection.NewNumeric([]float64{1.5, 2.5})\ntotal2 := c2.Sum()\ncollection.Dump(total2)\n// 4.000000 #float64\n```\n\n_Example: integers - empty collection_\n\n```go\nc3 := collection.NewNumeric([]int{})\ntotal3 := c3.Sum()\ncollection.Dump(total3)\n// 0 #int\n```\n\n## Construction\n\n### \u003ca id=\"clone\"\u003e\u003c/a\u003eClone · immutable · chainable\n\nClone returns a copy of the collection.\n\n_Example: basic cloning_\n\n```go\nc := collection.New([]int{1, 2, 3})\nclone := c.Clone()\n\nclone = clone.Append(4)\n\ncollection.Dump(c.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 3 #int\n// ]\n\ncollection.Dump(clone.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 3 #int\n//   3 =\u003e 4 #int\n// ]\n```\n\n_Example: branching pipelines_\n\n```go\nbase := collection.New([]int{1, 2, 3, 4, 5})\n\nevens := base.Clone().Filter(func(v int) bool {\n\treturn v%2 == 0\n})\n\nodds := base.Clone().Filter(func(v int) bool {\n\treturn v%2 != 0\n})\n\ncollection.Dump(base.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 3 #int\n//   3 =\u003e 4 #int\n//   4 =\u003e 5 #int\n// ]\n\ncollection.Dump(evens.Items())\n// #[]int [\n//   0 =\u003e 2 #int\n//   1 =\u003e 4 #int\n// ]\n\ncollection.Dump(odds.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 3 #int\n//   2 =\u003e 5 #int\n// ]\n```\n\n### \u003ca id=\"new\"\u003e\u003c/a\u003eNew · immutable · chainable\n\nNew creates a new Collection from the provided slice and borrows it.\n\n### \u003ca id=\"newnumeric\"\u003e\u003c/a\u003eNewNumeric · immutable · chainable\n\nNewNumeric wraps a slice of numeric types in a NumericCollection and borrows it.\n\n## Debugging\n\n### \u003ca id=\"dd\"\u003e\u003c/a\u003eDd · terminal\n\nDd prints items then terminates execution.\nLike Laravel's dd(), this is intended for debugging and\nshould not be used in production control flow.\n\n```go\nc := collection.New([]string{\"a\", \"b\"})\nc.Dd()\n// #[]string [\n//   0 =\u003e \"a\" #string\n//   1 =\u003e \"b\" #string\n// ]\n// Process finished with the exit code 1\n```\n\n### \u003ca id=\"dump\"\u003e\u003c/a\u003eDump · readonly · chainable\n\nDump prints items with godump and returns the same collection.\nThis is a no-op on the collection itself and never panics.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{1, 2, 3})\nc.Dump()\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 3 #int\n// ]\n```\n\n_Example: integers - chaining_\n\n```go\ncollection.New([]int{1, 2, 3}).\n\tFilter(func(v int) bool { return v \u003e 1 }).\n\tDump()\n// #[]int [\n//   0 =\u003e 2 #int\n//   1 =\u003e 3 #int\n// ]\n```\n\n_Example: integers_\n\n```go\nc2 := collection.New([]int{1, 2, 3})\ncollection.Dump(c2.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 3 #int\n// ]\n```\n\n### \u003ca id=\"dumpstr\"\u003e\u003c/a\u003eDumpStr · readonly · terminal\n\nDumpStr returns the pretty-printed dump of the items as a string,\nwithout printing or exiting.\nUseful for logging, snapshot testing, and non-interactive debugging.\n\n```go\nc := collection.New([]int{10, 20})\ns := c.DumpStr()\nfmt.Println(s)\n// #[]int [\n//   0 =\u003e 10 #int\n//   1 =\u003e 20 #int\n// ]\n```\n\n## Grouping\n\n### \u003ca id=\"groupby\"\u003e\u003c/a\u003eGroupBy · readonly · terminal\n\nGroupBy partitions the collection into groups keyed by the value\nreturned from keyFn.\n\n_Example: grouping integers by parity_\n\n```go\nvalues := []int{1, 2, 3, 4, 5}\n\ngroups := collection.GroupBy(\n\tcollection.New(values),\n\tfunc(v int) string {\n\t\tif v%2 == 0 {\n\t\t\treturn \"even\"\n\t\t}\n\t\treturn \"odd\"\n\t},\n)\n\ncollection.Dump(groups[\"even\"].Items())\n// #[]int [\n//  0 =\u003e 2 #int\n//  1 =\u003e 4 #int\n// ]\ncollection.Dump(groups[\"odd\"].Items())\n// #[]int [\n//  0 =\u003e 1 #int\n//  1 =\u003e 3 #int\n//  2 =\u003e 5 #int\n// ]\n```\n\n_Example: grouping structs by field_\n\n```go\ntype User struct {\n\t\tID   int\n\t\tRole string\n\t}\n\n\tusers := []User{\n\t\t{ID: 1, Role: \"admin\"},\n\t\t{ID: 2, Role: \"user\"},\n\t\t{ID: 3, Role: \"admin\"},\n\t}\n\n\tgroups2 := collection.GroupBy(\n\t\tcollection.New(users),\n\t\tfunc(u User) string { return u.Role },\n\t)\n\ncollection.Dump(groups2[\"admin\"].Items())\n// #[]main.User [\n//  0 =\u003e #main.User {\n//    +ID   =\u003e 1 #int\n//    +Role =\u003e \"admin\" #string\n//  }\n//  1 =\u003e #main.User {\n//    +ID   =\u003e 3 #int\n//    +Role =\u003e \"admin\" #string\n//  }\n// ]\ncollection.Dump(groups2[\"user\"].Items())\n// #[]main.User [\n//  0 =\u003e #main.User {\n//    +ID   =\u003e 2 #int\n//    +Role =\u003e \"user\" #string\n//  }\n// ]\n```\n\n### \u003ca id=\"groupbyslice\"\u003e\u003c/a\u003eGroupBySlice · readonly · terminal\n\nGroupBySlice partitions the collection into groups keyed by the value\nreturned from keyFn.\n\n_Example: grouping integers by parity_\n\n```go\nvalues := []int{1, 2, 3, 4, 5}\n\ngroups := collection.GroupBySlice(\n\tcollection.New(values),\n\tfunc(v int) string {\n\t\tif v%2 == 0 {\n\t\t\treturn \"even\"\n\t\t}\n\t\treturn \"odd\"\n\t},\n)\n\ncollection.Dump(groups[\"even\"])\n// #[]int [\n//  0 =\u003e 2 #int\n//  1 =\u003e 4 #int\n// ]\ncollection.Dump(groups[\"odd\"])\n// #[]int [\n//  0 =\u003e 1 #int\n//  1 =\u003e 3 #int\n//  2 =\u003e 5 #int\n// ]\n```\n\n_Example: grouping structs by field_\n\n```go\ntype User struct {\n\tID   int\n\tRole string\n}\n\nusers := []User{\n\t{ID: 1, Role: \"admin\"},\n\t{ID: 2, Role: \"user\"},\n\t{ID: 3, Role: \"admin\"},\n}\n\ngroups2 := collection.GroupBySlice(\n\tcollection.New(users),\n\tfunc(u User) string { return u.Role },\n)\n\ncollection.Dump(groups2[\"admin\"])\n// #[]main.User [\n//  0 =\u003e #main.User {\n//    +ID   =\u003e 1 #int\n//    +Role =\u003e \"admin\" #string\n//  }\n//  1 =\u003e #main.User {\n//    +ID   =\u003e 3 #int\n//    +Role =\u003e \"admin\" #string\n//  }\n// ]\ncollection.Dump(groups2[\"user\"])\n// #[]main.User [\n//  0 =\u003e #main.User {\n//    +ID   =\u003e 2 #int\n//    +Role =\u003e \"user\" #string\n//  }\n// ]\n```\n\n## Maps\n\n### \u003ca id=\"frommap\"\u003e\u003c/a\u003eFromMap · immutable · chainable\n\nFromMap materializes a map into a collection of key/value pairs.\n\n_Example: basic usage_\n\n```go\nm := map[string]int{\n\t\"a\": 1,\n\t\"b\": 2,\n\t\"c\": 3,\n}\n\nc := collection.FromMap(m)\nc.Sort(func(a, b collection.Pair[string, int]) bool {\n\treturn a.Key \u003c b.Key\n})\ncollection.Dump(c.Items())\n\n// #[]collection.Pair[string,int] [\n//   0 =\u003e #collection.Pair[string,int] {\n//     +Key   =\u003e \"a\" #string\n//     +Value =\u003e 1 #int\n//   }\n//   1 =\u003e #collection.Pair[string,int] {\n//     +Key   =\u003e \"b\" #string\n//     +Value =\u003e 2 #int\n//   }\n//   2 =\u003e #collection.Pair[string,int] {\n//     +Key   =\u003e \"c\" #string\n//     +Value =\u003e 3 #int\n//   }\n// ]\n```\n\n_Example: filtering map entries_\n\n```go\ntype Config struct {\n\tEnabled bool\n\tTimeout int\n}\n\nconfigs := map[string]Config{\n\t\"router-1\": {Enabled: true,  Timeout: 30},\n\t\"router-2\": {Enabled: false, Timeout: 10},\n\t\"router-3\": {Enabled: true,  Timeout: 45},\n}\n\nout := collection.\n\tFromMap(configs).\n\tFilter(func(p collection.Pair[string, Config]) bool {\n\t\treturn p.Value.Enabled\n\t}).\n\tSort(func(a, b collection.Pair[string, Config]) bool {\n\t\treturn a.Key \u003c b.Key\n\t}).\n\tItems()\n\ncollection.Dump(out)\n\n// #[]collection.Pair[string,main.Config·1] [\n//   0 =\u003e #collection.Pair[string,main.Config·1] {\n//     +Key       =\u003e \"router-1\" #string\n//     +Value     =\u003e #main.Config {\n//       +Enabled =\u003e true #bool\n//       +Timeout =\u003e 30 #int\n//     }\n//   }\n//   1 =\u003e #collection.Pair[string,main.Config·1] {\n//     +Key       =\u003e \"router-3\" #string\n//     +Value     =\u003e #main.Config {\n//       +Enabled =\u003e true #bool\n//       +Timeout =\u003e 45 #int\n//     }\n//   }\n// ]\n```\n\n_Example: map → collection → map_\n\n```go\nusers := map[string]int{\n\t\"alice\": 1,\n\t\"bob\":   2,\n}\n\nc2 := collection.FromMap(users)\nout2 := collection.ToMapKV(c2)\n\ncollection.Dump(out2)\n\n// #map[string]int {\n//   alice =\u003e 1 #int\n//   bob =\u003e 2 #int\n// }\n```\n\n### \u003ca id=\"tomap\"\u003e\u003c/a\u003eToMap · readonly · terminal\n\nToMap reduces a collection into a map using the provided key and value\nselector functions.\n\n_Example: basic usage_\n\n```go\nusers := []string{\"alice\", \"bob\", \"carol\"}\n\nout := collection.ToMap(\n\tcollection.New(users),\n\tfunc(name string) string { return name },\n\tfunc(name string) int { return len(name) },\n)\n\ncollection.Dump(out)\n// #map[string]int {\n//  alice =\u003e 5 #int\n//  bob =\u003e 3 #int\n//  carol =\u003e 5 #int\n// }\n```\n\n_Example: re-keying structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers2 := []User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n}\n\nbyID := collection.ToMap(\n\tcollection.New(users2),\n\tfunc(u User) int { return u.ID },\n\tfunc(u User) User { return u },\n)\n\ncollection.Dump(byID)\n// #map[int]main.User {\n//  1 =\u003e #main.User {\n//    +ID   =\u003e 1 #int\n//    +Name =\u003e \"Alice\" #string\n//  }\n//  2 =\u003e #main.User {\n//    +ID   =\u003e 2 #int\n//    +Name =\u003e \"Bob\" #string\n//  }\n// }\n```\n\n### \u003ca id=\"tomapkv\"\u003e\u003c/a\u003eToMapKV · readonly · terminal\n\nToMapKV converts a collection of key/value pairs into a map.\n\n_Example: basic usage_\n\n```go\nm := map[string]int{\n\t\"a\": 1,\n\t\"b\": 2,\n\t\"c\": 3,\n}\n\nc := collection.FromMap(m)\nout := collection.ToMapKV(c)\n\ncollection.Dump(out)\n\n// #map[string]int {\n//  a =\u003e 1 #int\n//  b =\u003e 2 #int\n//  c =\u003e 3 #int\n// }\n```\n\n_Example: filtering before conversion_\n\n```go\ntype Config struct {\n\tEnabled bool\n\tTimeout int\n}\n\nconfigs := map[string]Config{\n\t\"router-1\": {Enabled: true,  Timeout: 30},\n\t\"router-2\": {Enabled: false, Timeout: 10},\n\t\"router-3\": {Enabled: true,  Timeout: 45},\n}\n\nc2 := collection.\n\tFromMap(configs).\n\tFilter(func(p collection.Pair[string, Config]) bool {\n\t\treturn p.Value.Enabled\n\t})\n\nout2 := collection.ToMapKV(c2)\n\ncollection.Dump(out2)\n\n// #map[string]main.Config {\n//  router-1 =\u003e #main.Config {\n//    +Enabled =\u003e true #bool\n//    +Timeout =\u003e 30 #int\n//  }\n//  router-3 =\u003e #main.Config {\n//    +Enabled =\u003e true #bool\n//    +Timeout =\u003e 45 #int\n//  }\n// }\n```\n\n## Ordering\n\n### \u003ca id=\"after\"\u003e\u003c/a\u003eAfter · immutable · chainable\n\nAfter returns all items after the first element for which pred returns true.\nIf no element matches, an empty collection is returned.\n\n```go\nc := collection.New([]int{1, 2, 3, 4, 5})\nc.After(func(v int) bool { return v == 3 }).Dump()\n// #[]int [\n//  0 =\u003e 4 #int\n//  1 =\u003e 5 #int\n// ]\n```\n\n### \u003ca id=\"before\"\u003e\u003c/a\u003eBefore · immutable · chainable\n\nBefore returns a new collection containing all items that appear\n*before* the first element for which pred returns true.\n\n_Example: integers_\n\n```go\nc1 := collection.New([]int{1, 2, 3, 4, 5})\nout1 := c1.Before(func(v int) bool { return v \u003e= 3 })\ncollection.Dump(out1.Items())\n// #[]int [\n//  0 =\u003e 1 #int\n//  1 =\u003e 2 #int\n// ]\n```\n\n_Example: predicate never matches → whole collection returned_\n\n```go\nc2 := collection.New([]int{10, 20, 30})\nout2 := c2.Before(func(v int) bool { return v == 99 })\ncollection.Dump(out2.Items())\n// #[]int [\n//  0 =\u003e 10 #int\n//  1 =\u003e 20 #int\n//  2 =\u003e 30 #int\n// ]\n```\n\n_Example: structs: get all users before the first admin_\n\n```go\ntype User struct {\n\tName  string\n\tAdmin bool\n}\n\nc3 := collection.New([]User{\n\t{Name: \"Alice\", Admin: false},\n\t{Name: \"Bob\", Admin: false},\n\t{Name: \"Eve\", Admin: true},\n\t{Name: \"Mallory\", Admin: false},\n})\n\nout3 := c3.Before(func(u User) bool { return u.Admin })\ncollection.Dump(out3.Items())\n// #[]main.User [\n//  0 =\u003e #main.User {\n//    +Name  =\u003e \"Alice\" #string\n//    +Admin =\u003e false #bool\n//  }\n//  1 =\u003e #main.User {\n//    +Name  =\u003e \"Bob\" #string\n//    +Admin =\u003e false #bool\n//  }\n// ]\n```\n\n### \u003ca id=\"reverse\"\u003e\u003c/a\u003eReverse · mutable · chainable\n\nReverse reverses the order of items in the collection in place\nand returns the same collection for chaining.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{1, 2, 3, 4})\nc.Reverse()\ncollection.Dump(c.Items())\n// #[]int [\n//   0 =\u003e 4 #int\n//   1 =\u003e 3 #int\n//   2 =\u003e 2 #int\n//   3 =\u003e 1 #int\n// ]\n```\n\n_Example: strings – chaining_\n\n```go\nout := collection.New([]string{\"a\", \"b\", \"c\"}).\n\tReverse().\n\tAppend(\"d\").\n\tItems()\n\ncollection.Dump(out)\n// #[]string [\n//   0 =\u003e \"c\" #string\n//   1 =\u003e \"b\" #string\n//   2 =\u003e \"a\" #string\n//   3 =\u003e \"d\" #string\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID int\n}\n\nusers := collection.New([]User{\n\t{ID: 1},\n\t{ID: 2},\n\t{ID: 3},\n})\n\nusers.Reverse()\ncollection.Dump(users.Items())\n// #[]main.User [\n//   0 =\u003e #main.User {\n//     +ID =\u003e 3 #int\n//   }\n//   1 =\u003e #main.User {\n//     +ID =\u003e 2 #int\n//   }\n//   2 =\u003e #main.User {\n//     +ID =\u003e 1 #int\n//   }\n// ]\n```\n\n### \u003ca id=\"shuffle\"\u003e\u003c/a\u003eShuffle · mutable · chainable\n\nShuffle shuffles the collection in place and returns the same collection.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{1, 2, 3, 4, 5})\nc.Shuffle()\ncollection.Dump(c.Items())\n```\n\n_Example: strings – chaining_\n\n```go\nout2 := collection.New([]string{\"a\", \"b\", \"c\"}).\n\tShuffle().\n\tAppend(\"d\").\n\tItems()\n\ncollection.Dump(out2)\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID int\n}\n\nusers := collection.New([]User{\n\t{ID: 1},\n\t{ID: 2},\n\t{ID: 3},\n\t{ID: 4},\n})\n\nusers.Shuffle()\ncollection.Dump(users.Items())\n```\n\n### \u003ca id=\"sort\"\u003e\u003c/a\u003eSort · mutable · chainable\n\nSort sorts the collection in place using the provided comparison function and\nreturns the same collection for chaining.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{5, 1, 4, 2})\nc.Sort(func(a, b int) bool { return a \u003c b })\ncollection.Dump(c.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 4 #int\n//   3 =\u003e 5 #int\n// ]\n```\n\n_Example: strings (descending)_\n\n```go\nc2 := collection.New([]string{\"apple\", \"banana\", \"cherry\"})\nc2.Sort(func(a, b string) bool { return a \u003e b })\ncollection.Dump(c2.Items())\n// #[]string [\n//   0 =\u003e \"cherry\" #string\n//   1 =\u003e \"banana\" #string\n//   2 =\u003e \"apple\" #string\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tName string\n\tAge  int\n}\n\nusers := collection.New([]User{\n\t{Name: \"Alice\", Age: 30},\n\t{Name: \"Bob\", Age: 25},\n\t{Name: \"Carol\", Age: 40},\n})\n\n// Sort by age ascending\nusers.Sort(func(a, b User) bool {\n\treturn a.Age \u003c b.Age\n})\ncollection.Dump(users.Items())\n// #[]main.User [\n//   0 =\u003e #main.User {\n//     +Name =\u003e \"Bob\" #string\n//     +Age  =\u003e 25 #int\n//   }\n//   1 =\u003e #main.User {\n//     +Name =\u003e \"Alice\" #string\n//     +Age  =\u003e 30 #int\n//   }\n//   2 =\u003e #main.User {\n//     +Name =\u003e \"Carol\" #string\n//     +Age  =\u003e 40 #int\n//   }\n// ]\n```\n\n## Querying\n\n### \u003ca id=\"all\"\u003e\u003c/a\u003eAll · readonly · terminal\n\nAll returns true if fn returns true for every item in the collection.\nIf the collection is empty, All returns true (vacuously true).\n\n_Example: integers – all even_\n\n```go\nc := collection.New([]int{2, 4, 6})\nallEven := c.All(func(v int) bool { return v%2 == 0 })\ncollection.Dump(allEven)\n// true #bool\n```\n\n_Example: integers – not all even_\n\n```go\nc2 := collection.New([]int{2, 3, 4})\nallEven2 := c2.All(func(v int) bool { return v%2 == 0 })\ncollection.Dump(allEven2)\n// false #bool\n```\n\n_Example: strings – all non-empty_\n\n```go\nc3 := collection.New([]string{\"a\", \"b\", \"c\"})\nallNonEmpty := c3.All(func(s string) bool { return s != \"\" })\ncollection.Dump(allNonEmpty)\n// true #bool\n```\n\n_Example: empty collection (vacuously true)_\n\n```go\nempty := collection.New([]int{})\nall := empty.All(func(v int) bool { return v \u003e 0 })\ncollection.Dump(all)\n// true #bool\n```\n\n### \u003ca id=\"any\"\u003e\u003c/a\u003eAny · readonly · terminal\n\nAny returns true if at least one item satisfies fn.\n\n```go\nc := collection.New([]int{1, 2, 3, 4})\nhas := c.Any(func(v int) bool { return v%2 == 0 }) // true\ncollection.Dump(has)\n// true #bool\n```\n\n### \u003ca id=\"at\"\u003e\u003c/a\u003eAt · readonly · terminal\n\nAt returns the item at the given index and a boolean indicating\nwhether the index was within bounds.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{10, 20, 30})\nv, ok := c.At(1)\ncollection.Dump(v, ok)\n// 20 #int\n// true #bool\n```\n\n_Example: out of bounds_\n\n```go\nv2, ok2 := c.At(10)\ncollection.Dump(v2, ok2)\n// 0 #int\n// false #bool\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n})\n\nu, ok3 := users.At(0)\ncollection.Dump(u, ok3)\n// #main.User {\n//   +ID   =\u003e 1 #int\n//   +Name =\u003e \"Alice\" #string\n// }\n// true #bool\n```\n\n### \u003ca id=\"contains\"\u003e\u003c/a\u003eContains · readonly · terminal\n\nContains returns true if the collection contains the given value.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{1, 2, 3, 4, 5})\nhasTwo := collection.Contains(c, 2)\ncollection.Dump(hasTwo)\n// true #bool\n```\n\n_Example: strings_\n\n```go\nc2 := collection.New([]string{\"apple\", \"banana\", \"cherry\"})\nhasBanana := collection.Contains(c2, \"banana\")\ncollection.Dump(hasBanana)\n// true #bool\n```\n\n### \u003ca id=\"first\"\u003e\u003c/a\u003eFirst · readonly · terminal\n\nFirst returns the first element in the collection.\nIf the collection is empty, ok will be false.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{10, 20, 30})\n\nv, ok := c.First()\ncollection.Dump(v, ok)\n// 10 #int\n// true #bool\n```\n\n_Example: strings_\n\n```go\nc2 := collection.New([]string{\"alpha\", \"beta\", \"gamma\"})\n\nv2, ok2 := c2.First()\ncollection.Dump(v2, ok2)\n// \"alpha\" #string\n// true #bool\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n})\n\nu, ok3 := users.First()\ncollection.Dump(u, ok3)\n// #main.User {\n//   +ID   =\u003e 1 #int\n//   +Name =\u003e \"Alice\" #string\n// }\n// true #bool\n```\n\n_Example: integers - empty collection_\n\n```go\nc3 := collection.New([]int{})\nv3, ok4 := c3.First()\ncollection.Dump(v3, ok4)\n// 0 #int\n// false #bool\n```\n\n### \u003ca id=\"firstwhere\"\u003e\u003c/a\u003eFirstWhere · readonly · terminal\n\nFirstWhere returns the first item in the collection for which the provided\npredicate function returns true. If no items match, ok=false is returned\nalong with the zero value of T.\n\n```go\nnums := collection.New([]int{1, 2, 3, 4, 5})\nv, ok := nums.FirstWhere(func(n int) bool {\n\treturn n%2 == 0\n})\ncollection.Dump(v, ok)\n// 2 #int\n// true #bool\n\nv, ok = nums.FirstWhere(func(n int) bool {\n\treturn n \u003e 10\n})\ncollection.Dump(v, ok)\n// 0 #int\n// false #bool\n```\n\n### \u003ca id=\"indexwhere\"\u003e\u003c/a\u003eIndexWhere · readonly · terminal\n\nIndexWhere returns the index of the first item in the collection\nfor which the provided predicate function returns true.\nIf no item matches, it returns (0, false).\n\n_Example: integers_\n\n```go\nc := collection.New([]int{10, 20, 30, 40})\nidx, ok := c.IndexWhere(func(v int) bool { return v == 30 })\ncollection.Dump(idx, ok)\n// 2 #int\n// true #bool\n```\n\n_Example: not found_\n\n```go\nidx2, ok2 := c.IndexWhere(func(v int) bool { return v == 99 })\ncollection.Dump(idx2, ok2)\n// 0 #int\n// false #bool\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n\t{ID: 3, Name: \"Carol\"},\n})\n\nidx3, ok3 := users.IndexWhere(func(u User) bool {\n\treturn u.Name == \"Bob\"\n})\n\ncollection.Dump(idx3, ok3)\n// 1 #int\n// true #bool\n```\n\n### \u003ca id=\"isempty\"\u003e\u003c/a\u003eIsEmpty · readonly · terminal\n\nIsEmpty returns true if the collection has no items.\n\n_Example: integers (non-empty)_\n\n```go\nc := collection.New([]int{1, 2, 3})\n\nempty := c.IsEmpty()\ncollection.Dump(empty)\n// false #bool\n```\n\n_Example: strings (empty)_\n\n```go\nc2 := collection.New([]string{})\n\nempty2 := c2.IsEmpty()\ncollection.Dump(empty2)\n// true #bool\n```\n\n_Example: structs (non-empty)_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n})\n\nempty3 := users.IsEmpty()\ncollection.Dump(empty3)\n// false #bool\n```\n\n_Example: structs (empty)_\n\n```go\nnone := collection.New([]User{})\n\nempty4 := none.IsEmpty()\ncollection.Dump(empty4)\n// true #bool\n```\n\n### \u003ca id=\"last\"\u003e\u003c/a\u003eLast · readonly · terminal\n\nLast returns the last element in the collection.\nIf the collection is empty, ok will be false.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{10, 20, 30})\n\nv, ok := c.Last()\ncollection.Dump(v, ok)\n// 30 #int\n// true #bool\n```\n\n_Example: strings_\n\n```go\nc2 := collection.New([]string{\"alpha\", \"beta\", \"gamma\"})\n\nv2, ok2 := c2.Last()\ncollection.Dump(v2, ok2)\n// \"gamma\" #string\n// true #bool\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n\t{ID: 3, Name: \"Charlie\"},\n})\n\nu, ok3 := users.Last()\ncollection.Dump(u, ok3)\n// #main.User {\n//   +ID   =\u003e 3 #int\n//   +Name =\u003e \"Charlie\" #string\n// }\n// true #bool\n```\n\n_Example: empty collection_\n\n```go\nc3 := collection.New([]int{})\n\nv3, ok4 := c3.Last()\ncollection.Dump(v3, ok4)\n// 0 #int\n// false #bool\n```\n\n### \u003ca id=\"lastwhere\"\u003e\u003c/a\u003eLastWhere · readonly · terminal\n\nLastWhere returns the last element in the collection that satisfies the predicate fn.\nIf fn is nil, LastWhere returns the final element in the underlying slice.\nIf the collection is empty or no element matches, ok will be false.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{1, 2, 3, 4})\n\nv, ok := c.LastWhere(func(v int, i int) bool {\n\treturn v \u003c 3\n})\ncollection.Dump(v, ok)\n// 2 #int\n// true #bool\n```\n\n_Example: integers without predicate (equivalent to Last())_\n\n```go\nc2 := collection.New([]int{10, 20, 30, 40})\n\nv2, ok2 := c2.LastWhere(nil)\ncollection.Dump(v2, ok2)\n// 40 #int\n// true #bool\n```\n\n_Example: strings_\n\n```go\nc3 := collection.New([]string{\"alpha\", \"beta\", \"gamma\", \"delta\"})\n\nv3, ok3 := c3.LastWhere(func(s string, i int) bool {\n\treturn strings.HasPrefix(s, \"g\")\n})\ncollection.Dump(v3, ok3)\n// \"gamma\" #string\n// true #bool\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n\t{ID: 3, Name: \"Alex\"},\n\t{ID: 4, Name: \"Brian\"},\n})\n\nu, ok4 := users.LastWhere(func(u User, i int) bool {\n\treturn strings.HasPrefix(u.Name, \"A\")\n})\ncollection.Dump(u, ok4)\n// #main.User {\n//   +ID   =\u003e 3 #int\n//   +Name =\u003e \"Alex\" #string\n// }\n// true #bool\n```\n\n_Example: no matching element_\n\n```go\nc4 := collection.New([]int{5, 6, 7})\n\nv4, ok5 := c4.LastWhere(func(v int, i int) bool {\n\treturn v \u003e 10\n})\ncollection.Dump(v4, ok5)\n// 0 #int\n// false #bool\n```\n\n_Example: empty collection_\n\n```go\nc5 := collection.New([]int{})\n\nv5, ok6 := c5.LastWhere(nil)\ncollection.Dump(v5, ok6)\n// 0 #int\n// false #bool\n```\n\n### \u003ca id=\"none\"\u003e\u003c/a\u003eNone · readonly · terminal\n\nNone returns true if fn returns false for every item in the collection.\nIf the collection is empty, None returns true.\n\n_Example: integers – none even_\n\n```go\nc := collection.New([]int{1, 3, 5})\nnoneEven := c.None(func(v int) bool { return v%2 == 0 })\ncollection.Dump(noneEven)\n// true #bool\n```\n\n_Example: integers – some even_\n\n```go\nc2 := collection.New([]int{1, 2, 3})\nnoneEven2 := c2.None(func(v int) bool { return v%2 == 0 })\ncollection.Dump(noneEven2)\n// false #bool\n```\n\n_Example: empty collection_\n\n```go\nempty := collection.New([]int{})\nnone := empty.None(func(v int) bool { return v \u003e 0 })\ncollection.Dump(none)\n// true #bool\n```\n\n## Serialization\n\n### \u003ca id=\"tojson\"\u003e\u003c/a\u003eToJSON · readonly · terminal\n\nToJSON converts the collection's items into a compact JSON string.\n\n```go\npj1 := collection.New([]string{\"a\", \"b\"})\nout1, _ := pj1.ToJSON()\nfmt.Println(out1)\n// [\"a\",\"b\"]\n```\n\n### \u003ca id=\"toprettyjson\"\u003e\u003c/a\u003eToPrettyJSON · readonly · terminal\n\nToPrettyJSON converts the collection's items into a human-readable,\nindented JSON string.\n\n```go\npj1 := collection.New([]string{\"a\", \"b\"})\nout1, _ := pj1.ToPrettyJSON()\nfmt.Println(out1)\n// [\n//  \"a\",\n//  \"b\"\n// ]\n```\n\n## Set Operations\n\n### \u003ca id=\"difference\"\u003e\u003c/a\u003eDifference · immutable · chainable\n\nDifference returns a new collection containing elements from the first collection\nthat are not present in the second. Order follows the first collection, and\nduplicates are removed.\n\n_Example: integers_\n\n```go\na := collection.New([]int{1, 2, 2, 3, 4})\nb := collection.New([]int{2, 4})\n\nout := collection.Difference(a, b)\ncollection.Dump(out.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 3 #int\n// ]\n```\n\n_Example: strings_\n\n```go\nleft := collection.New([]string{\"apple\", \"banana\", \"cherry\"})\nright := collection.New([]string{\"banana\"})\n\nout2 := collection.Difference(left, right)\ncollection.Dump(out2.Items())\n// #[]string [\n//   0 =\u003e \"apple\" #string\n//   1 =\u003e \"cherry\" #string\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\ngroupA := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n\t{ID: 3, Name: \"Carol\"},\n})\n\ngroupB := collection.New([]User{\n\t{ID: 2, Name: \"Bob\"},\n})\n\nout3 := collection.Difference(groupA, groupB)\ncollection.Dump(out3.Items())\n// #[]main.User [\n//   0 =\u003e #main.User {\n//     +ID   =\u003e 1 #int\n//     +Name =\u003e \"Alice\" #string\n//   }\n//   1 =\u003e #main.User {\n//     +ID   =\u003e 3 #int\n//     +Name =\u003e \"Carol\" #string\n//   }\n// ]\n```\n\n### \u003ca id=\"intersect\"\u003e\u003c/a\u003eIntersect · immutable · chainable\n\nIntersect returns a new collection containing elements from the second\ncollection that are also present in the first.\n\n_Example: integers_\n\n```go\na := collection.New([]int{1, 2, 2, 3, 4})\nb := collection.New([]int{2, 4, 4, 5})\n\nout := collection.Intersect(a, b)\ncollection.Dump(out.Items())\n// #[]int [\n//   0 =\u003e 2 #int\n//   1 =\u003e 4 #int\n//   2 =\u003e 4 #int\n// ]\n```\n\n_Example: strings_\n\n```go\nleft := collection.New([]string{\"apple\", \"banana\", \"cherry\"})\nright := collection.New([]string{\"banana\", \"date\", \"cherry\", \"banana\"})\n\nout2 := collection.Intersect(left, right)\ncollection.Dump(out2.Items())\n// #[]string [\n//   0 =\u003e \"banana\" #string\n//   1 =\u003e \"cherry\" #string\n//   2 =\u003e \"banana\" #string\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\ngroupA := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n\t{ID: 3, Name: \"Carol\"},\n})\n\ngroupB := collection.New([]User{\n\t{ID: 2, Name: \"Bob\"},\n\t{ID: 3, Name: \"Carol\"},\n\t{ID: 4, Name: \"Dave\"},\n})\n\nout3 := collection.Intersect(groupA, groupB)\ncollection.Dump(out3.Items())\n// #[]main.User [\n//   0 =\u003e #main.User {\n//     +ID   =\u003e 2 #int\n//     +Name =\u003e \"Bob\" #string\n//   }\n//   1 =\u003e #main.User {\n//     +ID   =\u003e 3 #int\n//     +Name =\u003e \"Carol\" #string\n//   }\n// ]\n```\n\n### \u003ca id=\"symmetricdifference\"\u003e\u003c/a\u003eSymmetricDifference · immutable · chainable\n\nSymmetricDifference returns a new collection containing elements that appear\nin exactly one of the two collections. Order follows the first collection for\nits unique items, then the second for its unique items. Duplicates are removed.\n\n_Example: integers_\n\n```go\na := collection.New([]int{1, 2, 3, 3})\nb := collection.New([]int{3, 4, 4, 5})\n\nout := collection.SymmetricDifference(a, b)\ncollection.Dump(out.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 4 #int\n//   3 =\u003e 5 #int\n// ]\n```\n\n_Example: strings_\n\n```go\nleft := collection.New([]string{\"apple\", \"banana\"})\nright := collection.New([]string{\"banana\", \"date\"})\n\nout2 := collection.SymmetricDifference(left, right)\ncollection.Dump(out2.Items())\n// #[]string [\n//   0 =\u003e \"apple\" #string\n//   1 =\u003e \"date\" #string\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\ngroupA := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n})\n\ngroupB := collection.New([]User{\n\t{ID: 2, Name: \"Bob\"},\n\t{ID: 3, Name: \"Carol\"},\n})\n\nout3 := collection.SymmetricDifference(groupA, groupB)\ncollection.Dump(out3.Items())\n// #[]main.User [\n//   0 =\u003e #main.User {\n//     +ID   =\u003e 1 #int\n//     +Name =\u003e \"Alice\" #string\n//   }\n//   1 =\u003e #main.User {\n//     +ID   =\u003e 3 #int\n//     +Name =\u003e \"Carol\" #string\n//   }\n// ]\n```\n\n### \u003ca id=\"union\"\u003e\u003c/a\u003eUnion · immutable · chainable\n\nUnion returns a new collection containing the unique elements from both collections.\nItems from the first collection are kept in order, followed by items from the second\nthat were not already present.\n\n_Example: integers_\n\n```go\na := collection.New([]int{1, 2, 2, 3})\nb := collection.New([]int{3, 4, 4, 5})\n\nout := collection.Union(a, b)\ncollection.Dump(out.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 3 #int\n//   3 =\u003e 4 #int\n//   4 =\u003e 5 #int\n// ]\n```\n\n_Example: strings_\n\n```go\nleft := collection.New([]string{\"apple\", \"banana\"})\nright := collection.New([]string{\"banana\", \"date\"})\n\nout2 := collection.Union(left, right)\ncollection.Dump(out2.Items())\n// #[]string [\n//   0 =\u003e \"apple\" #string\n//   1 =\u003e \"banana\" #string\n//   2 =\u003e \"date\" #string\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\ngroupA := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n})\n\ngroupB := collection.New([]User{\n\t{ID: 2, Name: \"Bob\"},\n\t{ID: 3, Name: \"Carol\"},\n})\n\nout3 := collection.Union(groupA, groupB)\ncollection.Dump(out3.Items())\n// #[]main.User [\n//   0 =\u003e #main.User {\n//     +ID   =\u003e 1 #int\n//     +Name =\u003e \"Alice\" #string\n//   }\n//   1 =\u003e #main.User {\n//     +ID   =\u003e 2 #int\n//     +Name =\u003e \"Bob\" #string\n//   }\n//   2 =\u003e #main.User {\n//     +ID   =\u003e 3 #int\n//     +Name =\u003e \"Carol\" #string\n//   }\n// ]\n```\n\n### \u003ca id=\"unique\"\u003e\u003c/a\u003eUnique · immutable · chainable\n\nUnique returns a new collection with duplicate items removed, based on the\nequality function `eq`. The first occurrence of each unique value is kept,\nand order is preserved.\n\n_Example: integers_\n\n```go\nc1 := collection.New([]int{1, 2, 2, 3, 4, 4, 5})\nout1 := c1.Unique(func(a, b int) bool { return a == b })\ncollection.Dump(out1.Items())\n// #[]int [\n//\t0 =\u003e 1 #int\n//\t1 =\u003e 2 #int\n//\t2 =\u003e 3 #int\n//\t3 =\u003e 4 #int\n//\t4 =\u003e 5 #int\n// ]\n```\n\n_Example: strings (case-insensitive uniqueness)_\n\n```go\nc2 := collection.New([]string{\"A\", \"a\", \"B\", \"b\", \"A\"})\nout2 := c2.Unique(func(a, b string) bool {\n\treturn strings.EqualFold(a, b)\n})\ncollection.Dump(out2.Items())\n// #[]string [\n//\t0 =\u003e \"A\" #string\n//\t1 =\u003e \"B\" #string\n// ]\n```\n\n_Example: structs (unique by ID)_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nc3 := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n\t{ID: 1, Name: \"Alice Duplicate\"},\n})\n\nout3 := c3.Unique(func(a, b User) bool {\n\treturn a.ID == b.ID\n})\n\ncollection.Dump(out3.Items())\n// #[]main.User [\n//  0 =\u003e #main.User {\n//    +ID   =\u003e 1 #int\n//    +Name =\u003e \"Alice\" #string\n//  }\n//  1 =\u003e #main.User {\n//    +ID   =\u003e 2 #int\n//    +Name =\u003e \"Bob\" #string\n//  }\n// ]\n```\n\n### \u003ca id=\"uniqueby\"\u003e\u003c/a\u003eUniqueBy · immutable · chainable\n\nUniqueBy returns a new collection containing only the first occurrence\nof each element as determined by keyFn.\n\n_Example: structs – unique by ID_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n\t{ID: 1, Name: \"Alice Duplicate\"},\n})\n\nout := collection.UniqueBy(users, func(u User) int { return u.ID })\ncollection.Dump(out.Items())\n// #[]main.User [\n//  0 =\u003e #main.User {\n//    +ID   =\u003e 1 #int\n//    +Name =\u003e \"Alice\" #string\n//  }\n//  1 =\u003e #main.User {\n//    +ID   =\u003e 2 #int\n//    +Name =\u003e \"Bob\" #string\n//  }\n// ]\n```\n\n_Example: strings – case-insensitive uniqueness_\n\n```go\nvalues := collection.New([]string{\"A\", \"a\", \"B\", \"b\", \"A\"})\n\nout2 := collection.UniqueBy(values, func(s string) string {\n\treturn strings.ToLower(s)\n})\n\ncollection.Dump(out2.Items())\n// #[]string [\n//   0 =\u003e \"A\" #string\n//   1 =\u003e \"B\" #string\n// ]\n```\n\n_Example: integers – identity key_\n\n```go\nnums := collection.New([]int{3, 1, 2, 1, 3})\n\nout3 := collection.UniqueBy(nums, func(v int) int { return v })\ncollection.Dump(out3.Items())\n// #[]int [\n//   0 =\u003e 3 #int\n//   1 =\u003e 1 #int\n//   2 =\u003e 2 #int\n// ]\n```\n\n### \u003ca id=\"uniquecomparable\"\u003e\u003c/a\u003eUniqueComparable · immutable · chainable\n\nUniqueComparable returns a new collection with duplicate comparable items removed.\nThe first occurrence of each value is kept, and order is preserved.\nThis is a faster, allocation-friendly path for comparable types.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{1, 2, 2, 3, 4, 4, 5})\nout := collection.UniqueComparable(c)\ncollection.Dump(out.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 3 #int\n//   3 =\u003e 4 #int\n//   4 =\u003e 5 #int\n// ]\n```\n\n_Example: strings_\n\n```go\nc2 := collection.New([]string{\"A\", \"a\", \"B\", \"B\"})\nout2 := collection.UniqueComparable(c2)\ncollection.Dump(out2.Items())\n// #[]string [\n//   0 =\u003e \"A\" #string\n//   1 =\u003e \"a\" #string\n//   2 =\u003e \"B\" #string\n// ]\n```\n\n## Slicing\n\n### \u003ca id=\"chunk\"\u003e\u003c/a\u003eChunk · readonly · terminal\n\nChunk splits the collection into chunks of the given size.\nThe final chunk may be smaller if len(items) is not divisible by size.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{1, 2, 3, 4, 5}).Chunk(2)\ncollection.Dump(c)\n\n// #[][]int [\n//  0 =\u003e #[]int [\n//    0 =\u003e 1 #int\n//    1 =\u003e 2 #int\n//  ]\n//  1 =\u003e #[]int [\n//    0 =\u003e 3 #int\n//    1 =\u003e 4 #int\n//  ]\n//  2 =\u003e #[]int [\n//    0 =\u003e 5 #int\n//  ]\n//]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := []User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n\t{ID: 3, Name: \"Carol\"},\n\t{ID: 4, Name: \"Dave\"},\n}\n\nuserChunks := collection.New(users).Chunk(2)\ncollection.Dump(userChunks)\n\n// Dump output will show [][]User grouped in size-2 chunks, e.g.:\n// #[][]main.User [\n//  0 =\u003e #[]main.User [\n//    0 =\u003e #main.User {\n//      +ID   =\u003e 1 #int\n//      +Name =\u003e \"Alice\" #string\n//    }\n//    1 =\u003e #main.User {\n//      +ID   =\u003e 2 #int\n//      +Name =\u003e \"Bob\" #string\n//    }\n//  ]\n//  1 =\u003e #[]main.User [\n//    0 =\u003e #main.User {\n//      +ID   =\u003e 3 #int\n//      +Name =\u003e \"Carol\" #string\n//    }\n//    1 =\u003e #main.User {\n//      +ID   =\u003e 4 #int\n//      +Name =\u003e \"Dave\" #string\n//    }\n//  ]\n//]\n```\n\n### \u003ca id=\"filter\"\u003e\u003c/a\u003eFilter · mutable · chainable\n\nFilter keeps only the elements for which fn returns true.\nThis method mutates the collection in place and returns the same instance.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{1, 2, 3, 4})\nc.Filter(func(v int) bool {\n\treturn v%2 == 0\n})\ncollection.Dump(c.Items())\n// #[]int [\n//   0 =\u003e 2 #int\n//   1 =\u003e 4 #int\n// ]\n```\n\n_Example: strings_\n\n```go\nc2 := collection.New([]string{\"apple\", \"banana\", \"cherry\", \"avocado\"})\nc2.Filter(func(v string) bool {\n\treturn strings.HasPrefix(v, \"a\")\n})\ncollection.Dump(c2.Items())\n// #[]string [\n//   0 =\u003e \"apple\" #string\n//   1 =\u003e \"avocado\" #string\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n\t{ID: 3, Name: \"Andrew\"},\n\t{ID: 4, Name: \"Carol\"},\n})\n\nusers.Filter(func(u User) bool {\n\treturn strings.HasPrefix(u.Name, \"A\")\n})\n\ncollection.Dump(users.Items())\n// #[]main.User [\n//   0 =\u003e #main.User {\n//     +ID   =\u003e 1 #int\n//     +Name =\u003e \"Alice\" #string\n//   }\n//   1 =\u003e #main.User {\n//     +ID   =\u003e 3 #int\n//     +Name =\u003e \"Andrew\" #string\n//   }\n// ]\n```\n\n### \u003ca id=\"partition\"\u003e\u003c/a\u003ePartition · immutable · terminal\n\nPartition splits the collection into two new collections based on predicate fn.\nThe first collection contains items where fn returns true; the second contains\nitems where fn returns false. Order is preserved within each partition.\n\n_Example: integers - even/odd_\n\n```go\nnums := collection.New([]int{1, 2, 3, 4, 5})\nevens, odds := nums.Partition(func(n int) bool {\n\treturn n%2 == 0\n})\ncollection.Dump(evens.Items(), odds.Items())\n// #[]int [\n//   0 =\u003e 2 #int\n//   1 =\u003e 4 #int\n// ]\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 3 #int\n//   2 =\u003e 5 #int\n// ]\n```\n\n_Example: strings - prefix match_\n\n```go\nwords := collection.New([]string{\"go\", \"gopher\", \"rust\", \"ruby\"})\ngoWords, other := words.Partition(func(s string) bool {\n\treturn strings.HasPrefix(s, \"go\")\n})\ncollection.Dump(goWords.Items(), other.Items())\n// #[]string [\n//   0 =\u003e \"go\" #string\n//   1 =\u003e \"gopher\" #string\n// ]\n// #[]string [\n//   0 =\u003e \"rust\" #string\n//   1 =\u003e \"ruby\" #string\n// ]\n```\n\n_Example: structs - active vs inactive_\n\n```go\ntype User struct {\n\tName   string\n\tActive bool\n}\n\nusers := collection.New([]User{\n\t{Name: \"Alice\", Active: true},\n\t{Name: \"Bob\", Active: false},\n\t{Name: \"Carol\", Active: true},\n})\n\nactive, inactive := users.Partition(func(u User) bool {\n\treturn u.Active\n})\n\ncollection.Dump(active.Items(), inactive.Items())\n// #[]main.User [\n//   0 =\u003e #main.User {\n//     +Name   =\u003e \"Alice\" #string\n//     +Active =\u003e true #bool\n//   }\n//   1 =\u003e #main.User {\n//     +Name   =\u003e \"Carol\" #string\n//     +Active =\u003e true #bool\n//   }\n// ]\n// #[]main.User [\n//   0 =\u003e #main.User {\n//     +Name   =\u003e \"Bob\" #string\n//     +Active =\u003e false #bool\n//   }\n// ]\n```\n\n### \u003ca id=\"pop\"\u003e\u003c/a\u003ePop · mutable · terminal\n\nPop removes and returns the last item in the collection.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{1, 2, 3})\nitem, ok := c.Pop()\ncollection.Dump(item, ok, c.Items())\n// 3 #int\n// true #bool\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n// ]\n```\n\n_Example: strings_\n\n```go\nc2 := collection.New([]string{\"a\", \"b\", \"c\"})\nitem2, ok2 := c2.Pop()\ncollection.Dump(item2, ok2, c2.Items())\n// \"c\" #string\n// true #bool\n// #[]string [\n//   0 =\u003e \"a\" #string\n//   1 =\u003e \"b\" #string\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n})\n\nitem3, ok3 := users.Pop()\ncollection.Dump(item3, ok3, users.Items())\n// #main.User {\n//   +ID   =\u003e 2 #int\n//   +Name =\u003e \"Bob\" #string\n// }\n// true #bool\n// #[]main.User [\n//   0 =\u003e #main.User {\n//     +ID   =\u003e 1 #int\n//     +Name =\u003e \"Alice\" #string\n//   }\n// ]\n```\n\n_Example: empty collection_\n\n```go\nempty := collection.New([]int{})\nitem4, ok4 := empty.Pop()\ncollection.Dump(item4, ok4, empty.Items())\n// 0 #int\n// false #bool\n// #[]int [\n// ]\n```\n\n### \u003ca id=\"popn\"\u003e\u003c/a\u003ePopN · mutable · terminal\n\nPopN removes and returns the last n items in original order.\n\n_Example: integers – pop 2_\n\n```go\nc := collection.New([]int{1, 2, 3, 4})\npopped := c.PopN(2)\ncollection.Dump(popped, c.Items())\n// #[]int [\n//   0 =\u003e 3 #int\n//   1 =\u003e 4 #int\n// ]\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n// ]\n```\n\n_Example: strings – pop 1_\n\n```go\nc2 := collection.New([]string{\"a\", \"b\", \"c\"})\npopped2 := c2.PopN(1)\ncollection.Dump(popped2, c2.Items())\n// #[]string [\n//   0 =\u003e \"c\" #string\n// ]\n// #[]string [\n//   0 =\u003e \"a\" #string\n//   1 =\u003e \"b\" #string\n// ]\n```\n\n_Example: structs – pop 2_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n\t{ID: 3, Name: \"Carol\"},\n})\n\npopped3 := users.PopN(2)\ncollection.Dump(popped3, users.Items())\n// #[]main.User [\n//   0 =\u003e #main.User {\n//     +ID   =\u003e 2 #int\n//     +Name =\u003e \"Bob\" #string\n//   }\n//   1 =\u003e #main.User {\n//     +ID   =\u003e 3 #int\n//     +Name =\u003e \"Carol\" #string\n//   }\n// ]\n// #[]main.User [\n//   0 =\u003e #main.User {\n//     +ID   =\u003e 1 #int\n//     +Name =\u003e \"Alice\" #string\n//   }\n// ]\n```\n\n_Example: integers - n \u003c= 0 → returns nil, no change_\n\n```go\nc3 := collection.New([]int{1, 2, 3})\npopped4 := c3.PopN(0)\ncollection.Dump(popped4, c3.Items())\n// []int(nil)\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 3 #int\n// ]\n```\n\n_Example: strings - n exceeds length → all items popped, rest empty_\n\n```go\nc4 := collection.New([]string{\"x\", \"y\"})\npopped5 := c4.PopN(10)\ncollection.Dump(popped5, c4.Items())\n// #[]string [\n//   0 =\u003e \"x\" #string\n//   1 =\u003e \"y\" #string\n// ]\n// #[]string [\n// ]\n```\n\n### \u003ca id=\"skip\"\u003e\u003c/a\u003eSkip · immutable · chainable\n\nSkip returns a new collection with the first n items skipped.\nIf n is less than or equal to zero, Skip returns the full collection.\nIf n is greater than or equal to the collection length, Skip returns\nan empty collection.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{1, 2, 3, 4, 5})\nout := c.Skip(2)\ncollection.Dump(out.Items())\n// #[]int [\n//   0 =\u003e 3 #int\n//   1 =\u003e 4 #int\n//   2 =\u003e 5 #int\n// ]\n```\n\n_Example: skip none_\n\n```go\nout2 := c.Skip(0)\ncollection.Dump(out2.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 3 #int\n//   3 =\u003e 4 #int\n//   4 =\u003e 5 #int\n// ]\n```\n\n_Example: skip all_\n\n```go\nout3 := c.Skip(10)\ncollection.Dump(out3.Items())\n// #[]int [\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID int\n}\n\nusers := collection.New([]User{\n\t{ID: 1},\n\t{ID: 2},\n\t{ID: 3},\n})\n\nout4 := users.Skip(1)\ncollection.Dump(out4.Items())\n// #[]main.User [\n//  0 =\u003e #main.User {\n//    +ID =\u003e 2 #int\n//  }\n//  1 =\u003e #main.User {\n//    +ID =\u003e 3 #int\n//  }\n// ]\n```\n\n### \u003ca id=\"skiplast\"\u003e\u003c/a\u003eSkipLast · immutable · chainable\n\nSkipLast returns a new collection with the last n items skipped.\nIf n is less than or equal to zero, SkipLast returns the full collection.\nIf n is greater than or equal to the collection length, SkipLast returns\nan empty collection.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{1, 2, 3, 4, 5})\nout := c.SkipLast(2)\ncollection.Dump(out.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 3 #int\n// ]\n```\n\n_Example: skip none_\n\n```go\nout2 := c.SkipLast(0)\ncollection.Dump(out2.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 3 #int\n//   3 =\u003e 4 #int\n//   4 =\u003e 5 #int\n// ]\n```\n\n_Example: skip all_\n\n```go\nout3 := c.SkipLast(10)\ncollection.Dump(out3.Items())\n// #[]int [\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID int\n}\n\nusers := collection.New([]User{\n\t{ID: 1},\n\t{ID: 2},\n\t{ID: 3},\n})\n\nout4 := users.SkipLast(1)\ncollection.Dump(out4.Items())\n// #[]main.User [\n//  0 =\u003e #main.User {\n//    +ID =\u003e 1 #int\n//  }\n//  1 =\u003e #main.User {\n//    +ID =\u003e 2 #int\n//  }\n// ]\n```\n\n### \u003ca id=\"take\"\u003e\u003c/a\u003eTake · immutable · chainable\n\nTake returns a new collection containing the first `n` items when n \u003e 0,\nor the last `|n|` items when n \u003c 0.\n\n_Example: integers - take first 3_\n\n```go\nc1 := collection.New([]int{0, 1, 2, 3, 4, 5})\nout1 := c1.Take(3)\ncollection.Dump(out1.Items())\n// #[]int [\n//\t0 =\u003e 0 #int\n//\t1 =\u003e 1 #int\n//\t2 =\u003e 2 #int\n// ]\n```\n\n_Example: integers - take last 2 (negative n)_\n\n```go\nc2 := collection.New([]int{0, 1, 2, 3, 4, 5})\nout2 := c2.Take(-2)\ncollection.Dump(out2.Items())\n// #[]int [\n//\t0 =\u003e 4 #int\n//\t1 =\u003e 5 #int\n// ]\n```\n\n_Example: integers - n exceeds length → whole collection_\n\n```go\nc3 := collection.New([]int{10, 20})\nout3 := c3.Take(10)\ncollection.Dump(out3.Items())\n// #[]int [\n//\t0 =\u003e 10 #int\n//\t1 =\u003e 20 #int\n// ]\n```\n\n_Example: integers - zero → empty_\n\n```go\nc4 := collection.New([]int{1, 2, 3})\nout4 := c4.Take(0)\ncollection.Dump(out4.Items())\n// #[]int [\n// ]\n```\n\n### \u003ca id=\"takelast\"\u003e\u003c/a\u003eTakeLast · immutable · chainable\n\nTakeLast returns a new collection containing the last n items.\nIf n is less than or equal to zero, TakeLast returns an empty collection.\nIf n is greater than or equal to the collection length, TakeLast returns\nthe full collection.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{1, 2, 3, 4, 5})\nout := c.TakeLast(2)\ncollection.Dump(out.Items())\n// #[]int [\n//   0 =\u003e 4 #int\n//   1 =\u003e 5 #int\n// ]\n```\n\n_Example: take none_\n\n```go\nout2 := c.TakeLast(0)\ncollection.Dump(out2.Items())\n// #[]int [\n// ]\n```\n\n_Example: take all_\n\n```go\nout3 := c.TakeLast(10)\ncollection.Dump(out3.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 3 #int\n//   3 =\u003e 4 #int\n//   4 =\u003e 5 #int\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID int\n}\n\nusers := collection.New([]User{\n\t{ID: 1},\n\t{ID: 2},\n\t{ID: 3},\n})\n\nout4 := users.TakeLast(1)\ncollection.Dump(out4.Items())\n// #[]main.User [\n//  0 =\u003e #main.User {\n//    +ID =\u003e 3 #int\n//  }\n// ]\n```\n\n### \u003ca id=\"takeuntil\"\u003e\u003c/a\u003eTakeUntil · immutable · chainable\n\nTakeUntil returns items until the first element equals `value`.\nThe matching item is NOT included.\n\n_Example: integers - stop at value 3_\n\n```go\nc4 := collection.New([]int{1, 2, 3, 4})\nout4 := collection.TakeUntil(c4, 3)\ncollection.Dump(out4.Items())\n// #[]int [\n//\t0 =\u003e 1 #int\n//\t1 =\u003e 2 #int\n// ]\n```\n\n_Example: strings - value never appears → full slice_\n\n```go\nc5 := collection.New([]string{\"a\", \"b\", \"c\"})\nout5 := collection.TakeUntil(c5, \"x\")\ncollection.Dump(out5.Items())\n// #[]string [\n//\t0 =\u003e \"a\" #string\n//\t1 =\u003e \"b\" #string\n//\t2 =\u003e \"c\" #string\n// ]\n```\n\n_Example: integers - match is first item → empty result_\n\n```go\nc6 := collection.New([]int{9, 10, 11})\nout6 := collection.TakeUntil(c6, 9)\ncollection.Dump(out6.Items())\n// #[]int [\n// ]\n```\n\n### \u003ca id=\"takeuntilfn\"\u003e\u003c/a\u003eTakeUntilFn · immutable · chainable\n\nTakeUntilFn returns items until the predicate function returns true.\nThe matching item is NOT included.\n\n_Example: integers - stop when value \u003e= 3_\n\n```go\nc1 := collection.New([]int{1, 2, 3, 4})\nout1 := c1.TakeUntilFn(func(v int) bool { return v \u003e= 3 })\ncollection.Dump(out1.Items())\n// #[]int [\n//\t0 =\u003e 1 #int\n//\t1 =\u003e 2 #int\n// ]\n```\n\n_Example: integers - predicate immediately true → empty result_\n\n```go\nc2 := collection.New([]int{10, 20, 30})\nout2 := c2.TakeUntilFn(func(v int) bool { return v \u003c 50 })\ncollection.Dump(out2.Items())\n// #[]int [\n// ]\n```\n\n_Example: integers - no match → full list returned_\n\n```go\nc3 := collection.New([]int{1, 2, 3})\nout3 := c3.TakeUntilFn(func(v int) bool { return v == 99 })\ncollection.Dump(out3.Items())\n// #[]int [\n//\t0 =\u003e 1 #int\n//\t1 =\u003e 2 #int\n//\t2 =\u003e 3 #int\n// ]\n```\n\n### \u003ca id=\"window\"\u003e\u003c/a\u003eWindow · allocates · chainable\n\nWindow returns overlapping (or stepped) windows of the collection.\nEach window is a slice of length size; iteration advances by step (default 1 if step \u003c= 0).\nWindows that are shorter than size are omitted.\n\n_Example: integers - step 1_\n\n```go\nnums := collection.New([]int{1, 2, 3, 4, 5})\nwin := collection.Window(nums, 3, 1)\ncollection.Dump(win.Items())\n// #[][]int [\n//   0 =\u003e #[]int [\n//     0 =\u003e 1 #int\n//     1 =\u003e 2 #int\n//     2 =\u003e 3 #int\n//   ]\n//   1 =\u003e #[]int [\n//     0 =\u003e 2 #int\n//     1 =\u003e 3 #int\n//     2 =\u003e 4 #int\n//   ]\n//   2 =\u003e #[]int [\n//     0 =\u003e 3 #int\n//     1 =\u003e 4 #int\n//     2 =\u003e 5 #int\n//   ]\n// ]\n```\n\n_Example: strings - step 2_\n\n```go\nwords := collection.New([]string{\"a\", \"b\", \"c\", \"d\", \"e\"})\nwin2 := collection.Window(words, 2, 2)\ncollection.Dump(win2.Items())\n// #[][]string [\n//   0 =\u003e #[]string [\n//     0 =\u003e \"a\" #string\n//     1 =\u003e \"b\" #string\n//   ]\n//   1 =\u003e #[]string [\n//     0 =\u003e \"c\" #string\n//     1 =\u003e \"d\" #string\n//   ]\n// ]\n```\n\n_Example: structs_\n\n```go\ntype Point struct {\n\tX int\n\tY int\n}\n\npoints := collection.New([]Point{\n\t{X: 0, Y: 0},\n\t{X: 1, Y: 1},\n\t{X: 2, Y: 4},\n\t{X: 3, Y: 9},\n})\n\nwin3 := collection.Window(points, 2, 1)\ncollection.Dump(win3.Items())\n// #[][]main.Point [\n//   0 =\u003e #[]main.Point [\n//     0 =\u003e #main.Point {\n//       +X =\u003e 0 #int\n//       +Y =\u003e 0 #int\n//     }\n//     1 =\u003e #main.Point {\n//       +X =\u003e 1 #int\n//       +Y =\u003e 1 #int\n//     }\n//   ]\n//   1 =\u003e #[]main.Point [\n//     0 =\u003e #main.Point {\n//       +X =\u003e 1 #int\n//       +Y =\u003e 1 #int\n//     }\n//     1 =\u003e #main.Point {\n//       +X =\u003e 2 #int\n//       +Y =\u003e 4 #int\n//     }\n//   ]\n//   2 =\u003e #[]main.Point [\n//     0 =\u003e #main.Point {\n//       +X =\u003e 2 #int\n//       +Y =\u003e 4 #int\n//     }\n//     1 =\u003e #main.Point {\n//       +X =\u003e 3 #int\n//       +Y =\u003e 9 #int\n//     }\n//   ]\n// ]\n```\n\n## Transformation\n\n### \u003ca id=\"append\"\u003e\u003c/a\u003eAppend · immutable · chainable\n\nAppend returns a new collection with the given values appended.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{1, 2})\nc.Append(3, 4).Dump()\n// #[]int [\n//  0 =\u003e 1 #int\n//  1 =\u003e 2 #int\n//  2 =\u003e 3 #int\n//  3 =\u003e 4 #int\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n})\n\nusers.Append(\n\tUser{ID: 3, Name: \"Carol\"},\n\tUser{ID: 4, Name: \"Dave\"},\n).Dump()\n\n// #[]main.User [\n//  0 =\u003e #main.User {\n//    +ID   =\u003e 1 #int\n//    +Name =\u003e \"Alice\" #string\n//  }\n//  1 =\u003e #main.User {\n//    +ID   =\u003e 2 #int\n//    +Name =\u003e \"Bob\" #string\n//  }\n//  2 =\u003e #main.User {\n//    +ID   =\u003e 3 #int\n//    +Name =\u003e \"Carol\" #string\n//  }\n//  3 =\u003e #main.User {\n//    +ID   =\u003e 4 #int\n//    +Name =\u003e \"Dave\" #string\n//  }\n// ]\n```\n\n### \u003ca id=\"concat\"\u003e\u003c/a\u003eConcat · mutable · chainable\n\nConcat appends the values from the given slice onto the end of the collection,\n\n```go\nc := collection.New([]string{\"John Doe\"})\nconcatenated := c.\n\tConcat([]string{\"Jane Doe\"}).\n\tConcat([]string{\"Johnny Doe\"}).\n\tItems()\ncollection.Dump(concatenated)\n\n// #[]string [\n//  0 =\u003e \"John Doe\" #string\n//  1 =\u003e \"Jane Doe\" #string\n//  2 =\u003e \"Johnny Doe\" #string\n// ]\n```\n\n### \u003ca id=\"each\"\u003e\u003c/a\u003eEach · readonly · chainable\n\nEach runs fn for every item in the collection and returns the same collection,\nso it can be used in chains for side effects (logging, debugging, etc.).\n\n_Example: integers_\n\n```go\nc := collection.New([]int{1, 2, 3})\n\nsum := 0\nc.Each(func(v int) {\n\tsum += v\n})\n\ncollection.Dump(sum)\n// 6 #int\n```\n\n_Example: strings_\n\n```go\nc2 := collection.New([]string{\"apple\", \"banana\", \"cherry\"})\n\nvar out []string\nc2.Each(func(s string) {\n\tout = append(out, strings.ToUpper(s))\n})\n\ncollection.Dump(out)\n// #[]string [\n//   0 =\u003e \"APPLE\" #string\n//   1 =\u003e \"BANANA\" #string\n//   2 =\u003e \"CHERRY\" #string\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n\t{ID: 3, Name: \"Charlie\"},\n})\n\nvar names []string\nusers.Each(func(u User) {\n\tnames = append(names, u.Name)\n})\n\ncollection.Dump(names)\n// #[]string [\n//   0 =\u003e \"Alice\" #string\n//   1 =\u003e \"Bob\" #string\n//   2 =\u003e \"Charlie\" #string\n// ]\n```\n\n### \u003ca id=\"map\"\u003e\u003c/a\u003eMap · mutable · chainable\n\nMap applies a same-type transformation in place and returns the same collection.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{1, 2, 3})\n\nmapped := c.Map(func(v int) int {\n\treturn v * 10\n})\n\ncollection.Dump(mapped.Items())\n// #[]int [\n//   0 =\u003e 10 #int\n//   1 =\u003e 20 #int\n//   2 =\u003e 30 #int\n// ]\n```\n\n_Example: strings_\n\n```go\nc2 := collection.New([]string{\"apple\", \"banana\", \"cherry\"})\n\nupper := c2.Map(func(s string) string {\n\treturn strings.ToUpper(s)\n})\n\ncollection.Dump(upper.Items())\n// #[]string [\n//   0 =\u003e \"APPLE\" #string\n//   1 =\u003e \"BANANA\" #string\n//   2 =\u003e \"CHERRY\" #string\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n})\n\nupdated := users.Map(func(u User) User {\n\tu.Name = strings.ToUpper(u.Name)\n\treturn u\n})\n\ncollection.Dump(updated.Items())\n// #[]main.User [\n//   0 =\u003e #main.User {\n//     +ID   =\u003e 1 #int\n//     +Name =\u003e \"ALICE\" #string\n//   }\n//   1 =\u003e #main.User {\n//     +ID   =\u003e 2 #int\n//     +Name =\u003e \"BOB\" #string\n//   }\n// ]\n```\n\n### \u003ca id=\"mapto\"\u003e\u003c/a\u003eMapTo · immutable · chainable\n\nMapTo maps a Collection[T] to a Collection[R] using fn(T) R.\n\n_Example: integers - extract parity label_\n\n```go\nnums := collection.New([]int{1, 2, 3, 4})\nparity := collection.MapTo(nums, func(n int) string {\n\tif n%2 == 0 {\n\t\treturn \"even\"\n\t}\n\treturn \"odd\"\n})\ncollection.Dump(parity.Items())\n// #[]string [\n//   0 =\u003e \"odd\" #string\n//   1 =\u003e \"even\" #string\n//   2 =\u003e \"odd\" #string\n//   3 =\u003e \"even\" #string\n// ]\n```\n\n_Example: strings - length of each value_\n\n```go\nwords := collection.New([]string{\"go\", \"forj\", \"rocks\"})\nlengths := collection.MapTo(words, func(s string) int {\n\treturn len(s)\n})\ncollection.Dump(lengths.Items())\n// #[]int [\n//   0 =\u003e 2 #int\n//   1 =\u003e 4 #int\n//   2 =\u003e 5 #int\n// ]\n```\n\n_Example: structs - MapTo a field_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n})\n\nnames := collection.MapTo(users, func(u User) string {\n\treturn u.Name\n})\n\ncollection.Dump(names.Items())\n// #[]string [\n//   0 =\u003e \"Alice\" #string\n//   1 =\u003e \"Bob\" #string\n// ]\n```\n\n### \u003ca id=\"merge\"\u003e\u003c/a\u003eMerge · immutable · chainable\n\nMerge merges the given data into a new collection.\n\n_Example: integers - merging slices_\n\n```go\nints := collection.New([]int{1, 2})\nextra := []int{3, 4}\n// Merge the extra slice into the ints collection\nmerged1 := ints.Merge(extra)\ncollection.Dump(merged1.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 3 #int\n//   3 =\u003e 4 #int\n// ]\n```\n\n_Example: strings - merging another collection_\n\n```go\nstrs := collection.New([]string{\"a\", \"b\"})\nmore := collection.New([]string{\"c\", \"d\"})\n\nmerged2 := strs.Merge(more)\ncollection.Dump(merged2.Items())\n// #[]string [\n//   0 =\u003e \"a\" #string\n//   1 =\u003e \"b\" #string\n//   2 =\u003e \"c\" #string\n//   3 =\u003e \"d\" #string\n// ]\n```\n\n_Example: structs - merging struct slices_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n})\n\nmoreUsers := []User{\n\t{ID: 3, Name: \"Carol\"},\n\t{ID: 4, Name: \"Dave\"},\n}\n\nmerged3 := users.Merge(moreUsers)\ncollection.Dump(merged3.Items())\n// #[]main.User [\n//   0 =\u003e #main.User {\n//     +ID   =\u003e 1 #int\n//     +Name =\u003e \"Alice\" #string\n//   }\n//   1 =\u003e #main.User {\n//     +ID   =\u003e 2 #int\n//     +Name =\u003e \"Bob\" #string\n//   }\n//   2 =\u003e #main.User {\n//     +ID   =\u003e 3 #int\n//     +Name =\u003e \"Carol\" #string\n//   }\n//   3 =\u003e #main.User {\n//     +ID   =\u003e 4 #int\n//     +Name =\u003e \"Dave\" #string\n//   }\n// ]\n```\n\n### \u003ca id=\"multiply\"\u003e\u003c/a\u003eMultiply · immutable · chainable\n\nMultiply creates `n` copies of all items in the collection\nand returns a new collection.\n\n_Example: integers_\n\n```go\nints := collection.New([]int{1, 2})\nout := ints.Multiply(3)\ncollection.Dump(out.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 1 #int\n//   3 =\u003e 2 #int\n//   4 =\u003e 1 #int\n//   5 =\u003e 2 #int\n// ]\n```\n\n_Example: strings_\n\n```go\nstrs := collection.New([]string{\"a\", \"b\"})\nout2 := strs.Multiply(2)\ncollection.Dump(out2.Items())\n// #[]string [\n//   0 =\u003e \"a\" #string\n//   1 =\u003e \"b\" #string\n//   2 =\u003e \"a\" #string\n//   3 =\u003e \"b\" #string\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tName string\n}\n\nusers := collection.New([]User{{Name: \"Alice\"}, {Name: \"Bob\"}})\nout3 := users.Multiply(2)\ncollection.Dump(out3.Items())\n// #[]main.User [\n//   0 =\u003e #main.User {\n//     +Name =\u003e \"Alice\" #string\n//   }\n//   1 =\u003e #main.User {\n//     +Name =\u003e \"Bob\" #string\n//   }\n//   2 =\u003e #main.User {\n//     +Name =\u003e \"Alice\" #string\n//   }\n//   3 =\u003e #main.User {\n//     +Name =\u003e \"Bob\" #string\n//   }\n// ]\n```\n\n_Example: multiplying by zero or negative returns empty_\n\n```go\nnone := ints.Multiply(0)\ncollection.Dump(none.Items())\n// #[]int [\n// ]\n```\n\n### \u003ca id=\"pipe\"\u003e\u003c/a\u003ePipe · readonly · terminal\n\nPipe passes the entire collection into the given function\nand returns the function's result.\n\n_Example: integers – computing a sum_\n\n```go\nc := collection.New([]int{1, 2, 3})\nsum := collection.Pipe(c, func(col *collection.Collection[int]) int {\n\ttotal := 0\n\tfor _, v := range col.Items() {\n\t\ttotal += v\n\t}\n\treturn total\n})\ncollection.Dump(sum)\n// 6 #int\n```\n\n_Example: strings – joining values_\n\n```go\nc2 := collection.New([]string{\"a\", \"b\", \"c\"})\njoined := collection.Pipe(c2, func(col *collection.Collection[string]) string {\n\tout := \"\"\n\tfor _, v := range col.Items() {\n\t\tout += v\n\t}\n\treturn out\n})\ncollection.Dump(joined)\n// \"abc\" #string\n```\n\n_Example: structs – extracting just the names_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n})\n\nnames := collection.Pipe(users, func(col *collection.Collection[User]) []string {\n\tresult := make([]string, 0, len(col.Items()))\n\tfor _, u := range col.Items() {\n\t\tresult = append(result, u.Name)\n\t}\n\treturn result\n})\n\ncollection.Dump(names)\n// #[]string [\n//   0 =\u003e \"Alice\" #string\n//   1 =\u003e \"Bob\" #string\n// ]\n```\n\n### \u003ca id=\"prepend\"\u003e\u003c/a\u003ePrepend · mutable · chainable\n\nPrepend adds the given values to the beginning of the collection.\n\n_Example: integers_\n\n```go\nc := collection.New([]int{3, 4})\nc.Prepend(1, 2)\ncollection.Dump(c.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n//   2 =\u003e 3 #int\n//   3 =\u003e 4 #int\n// ]\n```\n\n_Example: strings_\n\n```go\nletters := collection.New([]string{\"c\", \"d\"})\nletters.Prepend(\"a\", \"b\")\ncollection.Dump(letters.Items())\n// #[]string [\n//   0 =\u003e \"a\" #string\n//   1 =\u003e \"b\" #string\n//   2 =\u003e \"c\" #string\n//   3 =\u003e \"d\" #string\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 2, Name: \"Bob\"},\n})\n\nusers.Prepend(User{ID: 1, Name: \"Alice\"})\ncollection.Dump(users.Items())\n// #[]main.User [\n//   0 =\u003e #main.User {\n//     +ID   =\u003e 1 #int\n//     +Name =\u003e \"Alice\" #string\n//   }\n//   1 =\u003e #main.User {\n//     +ID   =\u003e 2 #int\n//     +Name =\u003e \"Bob\" #string\n//   }\n// ]\n```\n\n_Example: integers - Prepending into an empty collection_\n\n```go\nempty := collection.New([]int{})\nempty.Prepend(9, 8)\ncollection.Dump(empty.Items())\n// #[]int [\n//   0 =\u003e 9 #int\n//   1 =\u003e 8 #int\n// ]\n```\n\n_Example: integers - Prepending no values → no change_\n\n```go\nc2 := collection.New([]int{1, 2})\nc2.Prepend()\ncollection.Dump(c2.Items())\n// #[]int [\n//   0 =\u003e 1 #int\n//   1 =\u003e 2 #int\n// ]\n```\n\n### \u003ca id=\"tap\"\u003e\u003c/a\u003eTap · immutable · chainable\n\nTap invokes fn with the collection pointer for side effects (logging, debugging,\ninspection) and returns the same collection to allow chaining.\n\n_Example: integers - capture intermediate state during a chain_\n\n```go\ncaptured1 := []int{}\nc1 := collection.New([]int{3, 1, 2}).\n\tSort(func(a, b int) bool { return a \u003c b }). // → [1, 2, 3]\n\tTap(func(col *collection.Collection[int]) {\n\t\tcaptured1 = append([]int(nil), col.Items()...) // snapshot copy\n\t}).\n\tFilter(func(v int) bool { return v \u003e= 2 }).\n\tDump()\n\t// #[]int [\n\t//  0 =\u003e 2 #int\n\t//  1 =\u003e 3 #int\n\t// ]\n\n// Use BOTH variables so nothing is \"declared and not used\"\ncollection.Dump(c1.Items())\ncollection.Dump(captured1)\n// #[]int [\n//  0 =\u003e 2 #int\n//  1 =\u003e 3 #int\n// ]\n// #[]int [\n//  0 =\u003e 1 #int\n//  1 =\u003e 2 #int\n//  2 =\u003e 3 #int\n// ]\n```\n\n_Example: integers - tap for debugging without changing flow_\n\n```go\nc2 := collection.New([]int{10, 20, 30}).\n\tTap(func(col *collection.Collection[int]) {\n\t\tcollection.Dump(col.Items())\n\t\t// #[]int [\n\t\t//  0 =\u003e 10 #int\n\t\t//  1 =\u003e 20 #int\n\t\t//  2 =\u003e 30 #int\n\t\t// ]\n\t}).\n\tFilter(func(v int) bool { return v \u003e 10 })\n\ncollection.Dump(c2.Items()) // ensures c2 is used\n// #[]int [\n//  0 =\u003e 20 #int\n//  1 =\u003e 30 #int\n// ]\n```\n\n_Example: structs - Tap with struct collection_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n})\n\nusers2 := users.Tap(func(col *collection.Collection[User]) {\n\tcollection.Dump(col.Items())\n\t// #[]main.User [\n\t//  0 =\u003e #main.User {\n\t//    +ID   =\u003e 1 #int\n\t//    +Name =\u003e \"Alice\" #string\n\t//  }\n\t//  1 =\u003e #main.User {\n\t//    +ID   =\u003e 2 #int\n\t//    +Name =\u003e \"Bob\" #string\n\t//  }\n\t// ]\n})\n\ncollection.Dump(users2.Items()) // ensures users2 is used\n// #[]main.User [\n//  0 =\u003e #main.User {\n//    +ID   =\u003e 1 #int\n//    +Name =\u003e \"Alice\" #string\n//  }\n//  1 =\u003e #main.User {\n//    +ID   =\u003e 2 #int\n//    +Name =\u003e \"Bob\" #string\n//  }\n// ]\n```\n\n### \u003ca id=\"times\"\u003e\u003c/a\u003eTimes · immutable · chainable\n\nTimes creates a new collection by calling fn(i) for i = 1..count.\nThis mirrors Laravel's Collection::times(), which is 1-indexed.\n\n_Example: integers - double each index_\n\n```go\ncTimes1 := collection.Times(5, func(i int) int {\n\treturn i * 2\n})\ncollection.Dump(cTimes1.Items())\n// #[]int [\n//\t0 =\u003e 2 #int\n//\t1 =\u003e 4 #int\n//\t2 =\u003e 6 #int\n//\t3 =\u003e 8 #int\n//\t4 =\u003e 10 #int\n// ]\n```\n\n_Example: strings_\n\n```go\ncTimes2 := collection.Times(3, func(i int) string {\n\treturn fmt.Sprintf(\"item-%d\", i)\n})\ncollection.Dump(cTimes2.Items())\n// #[]string [\n//\t0 =\u003e \"item-1\" #string\n//\t1 =\u003e \"item-2\" #string\n//\t2 =\u003e \"item-3\" #string\n// ]\n```\n\n_Example: structs_\n\n```go\ntype Point struct {\n\tX int\n\tY int\n}\n\ncTimes3 := collection.Times(4, func(i int) Point {\n\treturn Point{X: i, Y: i * i}\n})\ncollection.Dump(cTimes3.Items())\n// #[]main.Point [\n//\t0 =\u003e #main.Point {\n//\t\t+X =\u003e 1 #int\n//\t\t+Y =\u003e 1 #int\n//\t}\n//\t1 =\u003e #main.Point {\n//\t\t+X =\u003e 2 #int\n//\t\t+Y =\u003e 4 #int\n//\t}\n//\t2 =\u003e #main.Point {\n//\t\t+X =\u003e 3 #int\n//\t\t+Y =\u003e 9 #int\n//\t}\n//\t3 =\u003e #main.Point {\n//\t\t+X =\u003e 4 #int\n//\t\t+Y =\u003e 16 #int\n//\t}\n// ]\n```\n\n### \u003ca id=\"transform\"\u003e\u003c/a\u003eTransform · mutable · terminal\n\nTransform applies fn to every item *in place*, mutating the collection.\n\n_Example: integers_\n\n```go\nc1 := collection.New([]int{1, 2, 3})\nc1.Transform(func(v int) int { return v * 2 })\ncollection.Dump(c1.Items())\n// #[]int [\n//\t0 =\u003e 2 #int\n//\t1 =\u003e 4 #int\n//\t2 =\u003e 6 #int\n// ]\n```\n\n_Example: strings_\n\n```go\nc2 := collection.New([]string{\"a\", \"b\", \"c\"})\nc2.Transform(func(s string) string { return strings.ToUpper(s) })\ncollection.Dump(c2.Items())\n// #[]string [\n//\t0 =\u003e \"A\" #string\n//\t1 =\u003e \"B\" #string\n//\t2 =\u003e \"C\" #string\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nc3 := collection.New([]User{\n\t{ID: 1, Name: \"alice\"},\n\t{ID: 2, Name: \"bob\"},\n})\n\nc3.Transform(func(u User) User {\n\tu.Name = strings.ToUpper(u.Name)\n\treturn u\n})\n\ncollection.Dump(c3.Items())\n// #[]main.User [\n//  0 =\u003e #main.User {\n//    +ID   =\u003e 1 #int\n//    +Name =\u003e \"ALICE\" #string\n//  }\n//  1 =\u003e #main.User {\n//    +ID   =\u003e 2 #int\n//    +Name =\u003e \"BOB\" #string\n//  }\n// ]\n```\n\n### \u003ca id=\"zip\"\u003e\u003c/a\u003eZip · immutable · chainable\n\nZip combines two collections element-wise into a collection of tuples.\nThe resulting length is the smaller of the two inputs.\n\n_Example: integers and strings_\n\n```go\nnums := collection.New([]int{1, 2, 3})\nwords := collection.New([]string{\"one\", \"two\"})\n\nout := collection.Zip(nums, words)\ncollection.Dump(out.Items())\n// #[]collection.Tuple[int,string] [\n//   0 =\u003e #collection.Tuple[int,string] {\n//     +First  =\u003e 1 #int\n//     +Second =\u003e \"one\" #string\n//   }\n//   1 =\u003e #collection.Tuple[int,string] {\n//     +First  =\u003e 2 #int\n//     +Second =\u003e \"two\" #string\n//   }\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tID   int\n\tName string\n}\n\nusers := collection.New([]User{\n\t{ID: 1, Name: \"Alice\"},\n\t{ID: 2, Name: \"Bob\"},\n})\n\nroles := collection.New([]string{\"admin\", \"user\", \"extra\"})\n\nout2 := collection.Zip(users, roles)\ncollection.Dump(out2.Items())\n// #[]collection.Tuple[main.User·1,string] [\n//   0 =\u003e #collection.Tuple[main.User·1,string] {\n//     +First  =\u003e #main.User {\n//       +ID   =\u003e 1 #int\n//       +Name =\u003e \"Alice\" #string\n//     }\n//     +Second =\u003e \"admin\" #string\n//   }\n//   1 =\u003e #collection.Tuple[main.User·1,string] {\n//     +First  =\u003e #main.User {\n//       +ID   =\u003e 2 #int\n//       +Name =\u003e \"Bob\" #string\n//     }\n//     +Second =\u003e \"user\" #string\n//   }\n// ]\n```\n\n### \u003ca id=\"zipwith\"\u003e\u003c/a\u003eZipWith · immutable · chainable\n\nZipWith combines two collections element-wise using combiner fn.\nThe resulting length is the smaller of the two inputs.\n\n_Example: sum ints_\n\n```go\na := collection.New([]int{1, 2, 3})\nb := collection.New([]int{10, 20})\n\nout := collection.ZipWith(a, b, func(x, y int) int {\n\treturn x + y\n})\n\ncollection.Dump(out.Items())\n// #[]int [\n//   0 =\u003e 11 #int\n//   1 =\u003e 22 #int\n// ]\n```\n\n_Example: format strings_\n\n```go\nnames := collection.New([]string{\"alice\", \"bob\"})\nroles := collection.New([]string{\"admin\", \"user\", \"extra\"})\n\nout2 := collection.ZipWith(names, roles, func(name, role string) string {\n\treturn name + \":\" + role\n})\n\ncollection.Dump(out2.Items())\n// #[]string [\n//   0 =\u003e \"alice:admin\" #string\n//   1 =\u003e \"bob:user\" #string\n// ]\n```\n\n_Example: structs_\n\n```go\ntype User struct {\n\tName string\n}\n\ntype Role struct {\n\tTitle string\n}\n\nusers := collection.New([]User{{Name: \"Alice\"}, {Name: \"Bob\"}})\nroles2 := collection.New([]Role{{Title: \"admin\"}})\n\nout3 := collection.ZipWith(users, roles2, func(u User, r Role) string {\n\treturn u.Name + \" -\u003e \" + r.Title\n})\n\ncollection.Dump(out3.Items())\n// #[]string [\n//   0 =\u003e \"Alice -\u003e admin\" #string\n// ]\n```\n\u003c!-- api:embed:end --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoforj%2Fcollection","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgoforj%2Fcollection","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoforj%2Fcollection/lists"}