{"id":49267676,"url":"https://github.com/bold-minds/kv","last_synced_at":"2026-04-25T11:30:49.179Z","repository":{"id":349423860,"uuid":"1202161095","full_name":"bold-minds/kv","owner":"bold-minds","description":"Map operations Go's stdlib leaves out — Pick, Omit, Invert, Merge, Filter, Sort — on typed map[K]V. Zero deps.","archived":false,"fork":false,"pushed_at":"2026-04-05T22:15:32.000Z","size":56,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-05T22:23:27.991Z","etag":null,"topics":["filter","go","golang","hash-map","map-operations","pick-omit","sorted-keys"],"latest_commit_sha":null,"homepage":null,"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/bold-minds.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","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":"2026-04-05T17:18:27.000Z","updated_at":"2026-04-05T22:21:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bold-minds/kv","commit_stats":null,"previous_names":["bold-minds/kv"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/bold-minds/kv","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bold-minds%2Fkv","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bold-minds%2Fkv/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bold-minds%2Fkv/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bold-minds%2Fkv/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bold-minds","download_url":"https://codeload.github.com/bold-minds/kv/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bold-minds%2Fkv/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32260908,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-25T09:15:33.318Z","status":"ssl_error","status_checked_at":"2026-04-25T09:15:31.997Z","response_time":59,"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":["filter","go","golang","hash-map","map-operations","pick-omit","sorted-keys"],"created_at":"2026-04-25T11:30:48.610Z","updated_at":"2026-04-25T11:30:49.173Z","avatar_url":"https://github.com/bold-minds.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# kv\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/bold-minds/kv.svg)](https://pkg.go.dev/github.com/bold-minds/kv)\n[![Build](https://img.shields.io/github/actions/workflow/status/bold-minds/kv/test.yaml?branch=main\u0026label=tests)](https://github.com/bold-minds/kv/actions/workflows/test.yaml)\n[![Go Version](https://img.shields.io/github/go-mod/go-version/bold-minds/kv)](go.mod)\n\n**Map operations Go's stdlib leaves out — Pick, Omit, Invert, Merge, Filter, Sort — on typed `map[K]V`.**\n\nGo 1.21's `maps` package gave you `Keys`, `Values`, `Clone`, `Copy`, `DeleteFunc`, `Equal`, and a few others. Useful, but missing most of what you actually reach for when shaping data: keep-these-keys, drop-these-keys, invert, merge with override, filter by predicate, sorted key extraction. `kv` is the rest of the Ruby-style map vocabulary for typed Go maps.\n\n```go\n// Pick a subset of keys\ntrimmed := kv.Pick(user, \"id\", \"email\")\n\n// Merge with later maps overriding earlier\nfinal := kv.Merge(defaults, overrides, cliFlags)\n\n// Invert keys and values — K and V swap, return type is map[V]K\nbyName := kv.Invert(byID) // map[int]string → map[string]int\n\n// Keys sorted in true numeric order\nkeys := kv.SortedKeys(scores) // []int: [1, 2, 10, 20, 100]\n```\n\n## ✨ Why kv?\n\n- 🎯 **Operates on typed `map[K]V`** — no `map[string]any` bridge, no reflection in the hot path, full type safety at the call site.\n- 🧩 **Plain functions, not combinators** — every operation is a top-level generic function. Compose by nesting: `kv.Pick(kv.Omit(m, \"password\"), \"id\", \"email\")`.\n- 🔢 **Correct numeric sort** — `SortedKeys` / `SortedKeysDesc` use `cmp.Compare` via the `cmp.Ordered` constraint. A `map[int]T` sorts as `1, 2, 10, 20, 100`, not by codepoint.\n- 🛡️ **Immutable by default** — every function returns a new map or slice. Input is never mutated. Explicit `*InPlace` variants are available where in-place mutation is worth it.\n- 🪶 **Zero dependencies** — pure Go stdlib (`cmp`, `reflect`, `slices`). `reflect` is used only by `OmitValues` to permit non-comparable value types.\n\n## 📦 Installation\n\n```bash\ngo get github.com/bold-minds/kv\n```\n\nRequires Go 1.21 or later.\n\n## 🎯 Quick Start\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n\n    \"github.com/bold-minds/kv\"\n)\n\nfunc main() {\n    user := map[string]any{\n        \"id\":       42,\n        \"email\":    \"alice@example.com\",\n        \"password\": \"redacted\",\n        \"role\":     \"admin\",\n    }\n\n    // Strip sensitive fields before logging\n    safe := kv.Omit(user, \"password\")\n    fmt.Println(safe)\n\n    // Keep only whitelisted fields for a public API response\n    public := kv.Pick(user, \"id\", \"email\", \"role\")\n    fmt.Println(public)\n\n    // Merge config layers — later maps override earlier\n    defaults := map[string]int{\"retries\": 3, \"timeout\": 30, \"workers\": 1}\n    overrides := map[string]int{\"timeout\": 60}\n    final := kv.Merge(defaults, overrides)\n    fmt.Println(final) // retries:3, timeout:60, workers:1\n\n    // Sorted keys in true numeric order\n    scores := map[int]string{100: \"a\", 1: \"b\", 20: \"c\", 2: \"d\", 10: \"e\"}\n    fmt.Println(kv.SortedKeys(scores))\n    // → [1 2 10 20 100]\n}\n```\n\n## 📚 API\n\n### Map-shape operations (immutable)\n\n| Function | Purpose |\n|---|---|\n| `Pick[K, V](m, keys...) map[K]V` | New map containing only the listed keys |\n| `Omit[K, V](m, keys...) map[K]V` | New map with the listed keys removed |\n| `OmitValues[K, V](m, values...) map[K]V` | New map with entries whose values match any of `values` removed (uses `reflect.DeepEqual`, so non-comparable V types are fine) |\n| `Invert[K, V comparable](m) map[V]K` | Swap keys and values; if duplicate values exist, one arbitrary winner |\n| `Merge[K, V](maps...) map[K]V` | Shallow merge; later values override |\n| `Filter[K, V](m, pred) map[K]V` | New map containing entries where `pred(k, v)` is true |\n\n### Map-shape operations (in place)\n\n| Function | Purpose |\n|---|---|\n| `PickInPlace[K, V](m, keys...) map[K]V` | Delete every key not in `keys` from `m`, return `m` |\n| `OmitInPlace[K, V](m, keys...) map[K]V` | Delete the listed keys from `m`, return `m` |\n| `FilterInPlace[K, V](m, pred) map[K]V` | Delete entries where `pred(k, v)` is false, return `m` |\n\n`FilterInPlace` is the \"keep where true\" dual of stdlib `maps.DeleteFunc` (which is \"delete where true\"). Pick whichever polarity reads better at the call site.\n\n### Key extraction\n\n| Function | Purpose |\n|---|---|\n| `Keys[K, V](m) []K` | All keys, unspecified order |\n| `SortedKeys[K cmp.Ordered, V](m) []K` | Ascending order via `cmp.Compare` |\n| `SortedKeysDesc[K cmp.Ordered, V](m) []K` | Descending order |\n| `SortedKeysFunc[K, V](m, cmp func(a, b K) int) []K` | Custom comparator (for key types that aren't `cmp.Ordered`, e.g. `bool`, custom structs) |\n| `FilteredKeys[K, V](m, pred) []K` | Keys where `pred(k, v)` is true |\n\n### Value extraction\n\n| Function | Purpose |\n|---|---|\n| `Value[K, V](m, key) V` | `m[key]` or zero |\n| `ValueOr[K, V](m, key, def) V` | `m[key]` or caller default |\n| `Values[K, V](m, keys...) map[K]V` | Subset of `m` consisting of the supplied keys that exist. Returns a map (not a slice), preserving key↔value correspondence and making missing keys detectable. |\n\n## 🔍 How sorting works\n\n`SortedKeys` / `SortedKeysDesc` take `K cmp.Ordered`, so they accept any built-in ordered type: strings, all integer widths, and both float types. Sorting goes through `cmp.Compare`, which produces deterministic ordering even for `NaN` (NaN sorts before every non-NaN value).\n\nFor key types that are `comparable` but not `cmp.Ordered` — `bool`, custom structs, pointers, arrays — use `SortedKeysFunc` with your own comparator.\n\n## 🧭 `kv` vs `dig` vs stdlib `maps`\n\n| Use case | Reach for |\n|---|---|\n| Walk `map[string]any` / `map[any]any` / `[]any` from `json.Unmarshal` | [`bold-minds/dig`](https://github.com/bold-minds/dig) |\n| Pick/omit/invert/merge/filter on typed `map[K]V` | `kv` (this library) |\n| `Clone`, `Copy`, `DeleteFunc`, `Equal` | stdlib `maps` |\n| Sorted keys of a typed map | `kv.SortedKeys(m)` |\n\n`kv` and `dig` operate on different data shapes and are complementary — it's normal to import both.\n\n## 🔗 Related bold-minds libraries\n\n- [`bold-minds/dig`](https://github.com/bold-minds/dig) — nested-data navigation for `map[string]any` / `map[any]any` / `[]any`.\n- [`bold-minds/each`](https://github.com/bold-minds/each) — slice operations like find/filter/group.\n- [`bold-minds/list`](https://github.com/bold-minds/list) — set operations (union, intersect, difference) on slices. Useful on the results of `Keys`.\n- [`bold-minds/to`](https://github.com/bold-minds/to) — safe type conversion, for coercing extracted values.\n\n## 🚫 Non-goals\n\n- **No nested-map walker.** `kv` intentionally does not descend into nested maps or slices. That's [`bold-minds/dig`](https://github.com/bold-minds/dig)'s job. If you need both, import both.\n- **No `Must*` variants.** Every function either returns a zero/empty result on missing data or accepts a caller-supplied default.\n- **No reflection-based introspection.** The only use of `reflect` is `DeepEqual` inside `OmitValues`, to permit non-comparable V types. Everything else is plain generics.\n- **No cycles.** `kv` operates on the top level of a single map — there's nothing to cycle through.\n\n## 📄 License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbold-minds%2Fkv","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbold-minds%2Fkv","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbold-minds%2Fkv/lists"}