mapz

package
v1.2.2 Latest Latest
Warning

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

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

README

mapz

Generic map operations and utilities for Go

The mapz package provides 30+ utility functions for working with maps. All functions are generic and immutable (return new maps rather than modifying inputs, unless explicitly marked).

Quick Reference

By Category:

Installation

go get github.com/modfin/henry/mapz

Usage

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

scores := map[string]int{"Alice": 85, "Bob": 92, "Charlie": 78}

// Get all names
names := mapz.Keys(scores)
// names = []string{"Alice", "Bob", "Charlie"} (order not guaranteed)

// Filter high scores
highScorers := mapz.Filter(scores, func(name string, score int) bool {
    return score >= 80
})
// highScorers = {"Alice": 85, "Bob": 92}

Function Categories

Access & Extraction
Keys

Extract all keys as a slice.

scores := map[string]int{"Alice": 85, "Bob": 92}
names := mapz.Keys(scores)
// names = []string{"Alice", "Bob"} (order not guaranteed)
Values

Extract all values as a slice.

scores := map[string]int{"Alice": 85, "Bob": 92}
scoresList := mapz.Values(scores)
// scoresList = []int{85, 92} (order not guaranteed)
ValueOr

Get value or fallback.

scores := map[string]int{"Alice": 85}
alice := mapz.ValueOr(scores, "Alice", 0)      // alice = 85
charlie := mapz.ValueOr(scores, "Charlie", 0)  // charlie = 0 (fallback)
Comparison
Equal

Check if two maps are equal (keys and values match).

m1 := map[string]int{"a": 1, "b": 2}
m2 := map[string]int{"a": 1, "b": 2}
m3 := map[string]int{"a": 1, "b": 3}

mapz.Equal(m1, m2) // true
mapz.Equal(m1, m3) // false
EqualBy

Check equality with custom comparison function.

m1 := map[string]int{"a": 1, "b": 2}
m2 := map[string]int{"a": 2, "b": 4}

// Check if values are all even
mapz.EqualBy(m1, m2, func(v1, v2 int) bool {
    return v1%2 == v2%2
}) // true (both 1→2 and 2→4 are odd→even)
Cloning & Copying
Clone

Create a shallow copy of a map.

original := map[string]int{"a": 1, "b": 2}
copy := mapz.Clone(original)
// copy = {"a": 1, "b": 2}
// Modifying copy doesn't affect original
Copy

Copy all entries from source to destination.

dst := map[string]int{"a": 1}
src := map[string]int{"b": 2, "c": 3}
mapz.Copy(dst, src)
// dst = {"a": 1, "b": 2, "c": 3}
Clear

Remove all entries from a map (mutates).

m := map[string]int{"a": 1, "b": 2}
mapz.Clear(m)
// m = {}
Combination
Merge

Merge multiple maps into one.

m1 := map[string]int{"a": 1, "b": 2}
m2 := map[string]int{"b": 20, "c": 3}  // Note: b has different value
m3 := map[string]int{"d": 4}

merged := mapz.Merge(m1, m2, m3)
// merged = {"a": 1, "b": 20, "c": 3, "d": 4}
// Later maps overwrite earlier ones for duplicate keys
MergeWith

Merge with custom conflict resolution.

m1 := map[string]int{"a": 1, "b": 2}
m2 := map[string]int{"b": 3, "c": 4}

// Sum values for duplicate keys
merged := mapz.MergeWith([]map[string]int{m1, m2}, func(vals ...int) int {
    sum := 0
    for _, v := range vals {
        sum += v
    }
    return sum
})
// merged = {"a": 1, "b": 5, "c": 4}  (2 + 3 = 5)
Filtering
Filter

Keep entries matching predicate.

scores := map[string]int{"Alice": 85, "Bob": 72, "Charlie": 90}
highScorers := mapz.Filter(scores, func(name string, score int) bool {
    return score >= 80
})
// highScorers = {"Alice": 85, "Charlie": 90}
FilterByKeys

Keep only specified keys.

scores := map[string]int{"Alice": 85, "Bob": 72, "Charlie": 90}
selected := mapz.FilterByKeys(scores, []string{"Alice", "Charlie", "Dave"})
// selected = {"Alice": 85, "Charlie": 90}
// Dave is ignored (doesn't exist in original)
FilterByValues

Keep only entries with specified values.

scores := map[string]int{"Alice": 85, "Bob": 90, "Charlie": 90}
in90s := mapz.FilterByValues(scores, []int{90})
// in90s = {"Bob": 90, "Charlie": 90}
Reject

Remove entries matching predicate (inverse of Filter).

scores := map[string]int{"Alice": 85, "Bob": 72, "Charlie": 90}
passing := mapz.Reject(scores, func(name string, score int) bool {
    return score < 80
})
// passing = {"Alice": 85, "Charlie": 90}
RejectByKeys

Remove specified keys.

scores := map[string]int{"Alice": 85, "Bob": 72, "Charlie": 90}
withoutBob := mapz.RejectByKeys(scores, []string{"Bob"})
// withoutBob = {"Alice": 85, "Charlie": 90}
RejectByValues

Remove entries with specified values.

scores := map[string]int{"Alice": 85, "Bob": 72, "Charlie": 72}
without72s := mapz.RejectByValues(scores, []int{72})
// without72s = {"Alice": 85}
Delete

Remove entries where predicate is true (mutates).

scores := map[string]int{"Alice": 85, "Bob": 72, "Charlie": 90}
mapz.Delete(scores, func(name string, score int) bool {
    return score < 80
})
// scores = {"Alice": 85, "Charlie": 90}
DeleteKeys

Remove entries by keys (mutates).

scores := map[string]int{"Alice": 85, "Bob": 72, "Charlie": 90}
mapz.DeleteKeys(scores, "Bob")
// scores = {"Alice": 85, "Charlie": 90}
DeleteValues

Remove entries by values (mutates).

scores := map[string]int{"Alice": 85, "Bob": 72, "Charlie": 72}
mapz.DeleteValues(scores, 72)
// scores = {"Alice": 85}
Transformation
MapKeys

Transform all keys.

scores := map[string]int{"alice": 85, "bob": 92}
upperKeys := mapz.MapKeys(scores, strings.ToUpper)
// upperKeys = {"ALICE": 85, "BOB": 92}
MapValues

Transform all values.

scores := map[string]int{"Alice": 85, "Bob": 92}
doubled := mapz.MapValues(scores, func(score int) int {
    return score * 2
})
// doubled = {"Alice": 170, "Bob": 184}
Remap

Transform both keys and values.

scores := map[string]int{"Alice": 85, "Bob": 92}
remapped := mapz.Remap(scores, func(name string, score int) (string, string) {
    return strings.ToUpper(name), fmt.Sprintf("%d%%", score)
})
// remapped = {"ALICE": "85%", "BOB": "92%"}
RemapKeys

Transform only keys.

ids := map[int]string{1: "Alice", 2: "Bob"}
withPrefix := mapz.RemapKeys(ids, func(id int, name string) string {
    return fmt.Sprintf("user-%d", id)
})
// withPrefix = {"user-1": "Alice", "user-2": "Bob"}
RemapValues

Transform only values (like MapValues but receives key too).

scores := map[string]int{"Alice": 85, "Bob": 92}
withGrades := mapz.RemapValues(scores, func(name string, score int) string {
    if score >= 90 {
        return "A"
    } else if score >= 80 {
        return "B"
    }
    return "C"
})
// withGrades = {"Alice": "B", "Bob": "A"}
Invert

Swap keys and values.

scores := map[string]int{"Alice": 85, "Bob": 92}
byScore := mapz.Invert(scores)
// byScore = map[int]string{85: "Alice", 92: "Bob"}
// Note: If duplicate values exist, last one wins
Update Operations
Update

Update value at key if it exists.

counters := map[string]int{"visits": 0, "clicks": 5}
updated := mapz.Update(counters, "visits", func(v int) int {
    return v + 1
})
// updated = true, counters = {"visits": 1, "clicks": 5}

// Key doesn't exist
updated = mapz.Update(counters, "downloads", func(v int) int {
    return v + 1
})
// updated = false, counters unchanged
GetOrSet

Get value or compute and set if missing.

cache := map[string]string{}

// First call - computes and caches
value := mapz.GetOrSet(cache, "config", func() string {
    // Expensive operation
    return loadConfigFromDisk()
})

// Second call - returns cached value
value = mapz.GetOrSet(cache, "config", func() string {
    // This function is NOT called!
    return "never used"
})
Set Operations
Difference

Keys in first map but not in second.

m1 := map[string]int{"a": 1, "b": 2, "c": 3}
m2 := map[string]int{"b": 20, "d": 4}
diff := mapz.Difference(m1, m2)
// diff = {"a": 1, "c": 3}
Intersection

Keys present in both maps.

m1 := map[string]int{"a": 1, "b": 2, "c": 3}
m2 := map[string]int{"b": 20, "c": 30, "d": 4}
common := mapz.Intersection(m1, m2)
// common = {"b": 2, "c": 3}
// Values from m1 are used
Conversion
Slice

Convert map to slice.

scores := map[string]int{"Alice": 85, "Bob": 92}
pairs := mapz.Slice(scores, func(name string, score int) string {
    return fmt.Sprintf("%s=%d", name, score)
})
// pairs = []string{"Alice=85", "Bob=92"} (order not guaranteed)
Entry

Type representing a key-value pair.

type Entry[K comparable, V any] struct {
    Key   K
    Value V
}
Entries

Convert map to slice of entries.

scores := map[string]int{"Alice": 85, "Bob": 92}
entries := mapz.Entries(scores)
// entries = []Entry{{"Alice", 85}, {"Bob", 92}}
FromEntries

Convert entries back to map.

entries := []mapz.Entry[string, int]{
    {Key: "Alice", Value: 85},
    {Key: "Bob", Value: 92},
}
m := mapz.FromEntries(entries)
// m = {"Alice": 85, "Bob": 92}

Performance Notes

  • Immutable by default: Functions return new maps (safer, easier to reason about)
  • Pre-allocation: Merge and Clone pre-allocate capacity to avoid reallocations
  • Mutation marked: Functions that mutate (Clear, Delete, etc.) are clearly documented

See Also

  • setz - Dedicated set data structure
  • slicez - Work with slice representations of map data

Documentation

Overview

Package mapz provides utility functions for working with maps.

The package includes operations for:

  • Extracting keys and values
  • Comparing maps for equality
  • Filtering and transforming maps
  • Merging and manipulating map entries
  • Converting between maps and slices

Most functions that return maps create new maps (immutable operations). Functions that mutate maps are clearly marked with "Warning mutates" in their documentation.

Example usage:

m := map[string]int{"a": 1, "b": 2, "c": 3}

// Filter values greater than 1
filtered := mapz.Filter(m, func(k string, v int) bool { return v > 1 })
// filtered = map[string]int{"b": 2, "c": 3}

// Transform values
doubled := mapz.MapValues(m, func(v int) int { return v * 2 })
// doubled = map[string]int{"a": 2, "b": 4, "c": 6}

// Merge maps
m2 := map[string]int{"d": 4}
merged := mapz.Merge(m, m2)
// merged = map[string]int{"a": 1, "b": 2, "c": 3, "d": 4}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Clear

func Clear[K comparable, V any](m map[K]V)

Clear will delete all elements from a map Warning mutates map

func Clone

func Clone[K comparable, V any](m map[K]V) map[K]V

Clone will copy all keys and values of a map in to a new one

func Copy

func Copy[K comparable, V any](dst, src map[K]V)

Copy will copy all entries in src into det Warning mutates map

func Delete

func Delete[K comparable, V any](m map[K]V, del func(K, V) bool)

Delete will remove all entries from a map where the del function returns true Warning mutates map, , use Filter or Reject for immutable version

func DeleteKeys

func DeleteKeys[K comparable, V any](m map[K]V, needles ...K)

DeleteKeys will remove all instances where the needles matches a key in the map Warning mutates map, use Filter or Reject for immutable version

func DeleteValues

func DeleteValues[K comparable, V comparable](m map[K]V, needles ...V)

DeleteValues will remove all instances where the needle matches a value in the map Warning mutates map, use Filter or Reject for immutable version

func Difference added in v1.2.0

func Difference[K comparable, V any](m1, m2 map[K]V) map[K]V

Difference returns a new map containing only the key-value pairs from m1 that are not present in m2 (keys in m1 but not in m2).

Example:

m1 := map[string]int{"a": 1, "b": 2, "c": 3}
m2 := map[string]int{"b": 20, "d": 4}
Difference(m1, m2) // Returns map[string]int{"a": 1, "c": 3}

func Equal

func Equal[K, V comparable](m1, m2 map[K]V) bool

Equal returns true if all key are present in both maps and map to the same value

func EqualBy

func EqualBy[K comparable, V1, V2 any](m1 map[K]V1, m2 map[K]V2, eq func(V1, V2) bool) bool

EqualBy returns true if all key are present in both maps and map to the same value, determined by the "eq" func

func Filter

func Filter[K comparable, V any](m map[K]V, pick func(key K, val V) bool) map[K]V

Filter returns a new map containing only entries that satisfy the predicate. The predicate receives both the key and value for each entry.

Example:

m := map[string]int{"a": 1, "b": 2, "c": 3, "d": 4}
mapz.Filter(m, func(k string, v int) bool { return v > 2 })
// Returns map[string]int{"c": 3, "d": 4}

mapz.Filter(m, func(k string, v int) bool { return k == "a" || k == "c" })
// Returns map[string]int{"a": 1, "c": 3}

func FilterByKeys

func FilterByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V

FilterByKeys returns a new map containing only entries with keys in the provided slice.

Example:

m := map[string]int{"a": 1, "b": 2, "c": 3, "d": 4}
mapz.FilterByKeys(m, []string{"a", "c", "e"})
// Returns map[string]int{"a": 1, "c": 3} (ignores "e" since it doesn't exist)

func FilterByValues

func FilterByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V

FilterByValues returns a new map containing only entries with values in the provided slice.

Example:

m := map[string]int{"a": 1, "b": 2, "c": 3, "d": 2}
mapz.FilterByValues(m, []int{1, 2})
// Returns map[string]int{"a": 1, "b": 2, "d": 2}

func FromEntries

func FromEntries[K comparable, V any](slice []Entry[K, V]) map[K]V

FromEntries converts a slice of Entry structs back to a map. If duplicate keys exist, the last entry wins.

Example:

entries := []Entry[string, int]{{Key: "a", Value: 1}, {Key: "b", Value: 2}}
m := mapz.FromEntries(entries)
// Returns map[string]int{"a": 1, "b": 2}

func GetOrSet added in v1.2.0

func GetOrSet[K comparable, V any](m map[K]V, key K, compute func() V) V

GetOrSet retrieves the value for the given key, or if the key doesn't exist, computes the value using the provided function, sets it in the map, and returns it.

Example:

m := map[string]int{}
v1 := GetOrSet(m, "key", func() int { return expensiveComputation() })
v2 := GetOrSet(m, "key", func() int { return 999 }) // Returns same value as v1, func not called

func Intersection added in v1.2.0

func Intersection[K comparable, V any](m1, m2 map[K]V) map[K]V

Intersection returns a new map containing only the key-value pairs that exist in both m1 and m2. Values from m1 are used.

Example:

m1 := map[string]int{"a": 1, "b": 2, "c": 3}
m2 := map[string]int{"b": 20, "c": 30, "d": 4}
Intersection(m1, m2) // Returns map[string]int{"b": 2, "c": 3}

func Invert

func Invert[K, V comparable](m map[K]V) map[V]K

Invert swaps keys and values in a map. The map values must be comparable to become keys in the result. If duplicate values exist, the last one wins (since map keys are unique).

Example:

m := map[string]int{"a": 1, "b": 2, "c": 3}
mapz.Invert(m)
// Returns map[int]string{1: "a", 2: "b", 3: "c"}

// With duplicate values (last one wins)
m2 := map[string]int{"a": 1, "b": 1}
mapz.Invert(m2)
// Returns map[int]string{1: "b"} ("b" overwrites "a")

func Keys

func Keys[K comparable, V any](m map[K]V) []K

Keys returns all keys in a map in a none deterministic order

func MapKeys added in v1.2.0

func MapKeys[K1 comparable, V any, K2 comparable](m map[K1]V, mapper func(K1) K2) map[K2]V

MapKeys transforms the keys of a map while keeping the same values. The mapper function receives only the key (unlike RemapKeys which receives both key and value).

Example:

MapKeys(map[string]int{"a": 1, "b": 2}, strings.ToUpper)
// Returns map[string]int{"A": 1, "B": 2}

func MapValues added in v1.2.0

func MapValues[K comparable, V1 any, V2 any](m map[K]V1, mapper func(V1) V2) map[K]V2

MapValues transforms the values of a map while keeping the same keys. The mapper function receives only the value (unlike RemapValues which receives both key and value).

Example:

MapValues(map[string]int{"a": 1, "b": 2}, func(v int) int { return v * 2 })
// Returns map[string]int{"a": 2, "b": 4}

func Merge

func Merge[K comparable, V any](maps ...map[K]V) map[K]V

Merge multiple maps from left to right into a new map.

func MergeWith added in v1.2.0

func MergeWith[K comparable, V any](maps []map[K]V, merge func(values ...V) V) map[K]V

MergeWith merges multiple maps, using the merge function to resolve key conflicts. When the same key exists in multiple maps, the merge function is called with all values for that key to produce the final value.

Example:

m1 := map[string]int{"a": 1, "b": 2}
m2 := map[string]int{"b": 3, "c": 4}
MergeWith(m1, m2, func(v1, v2 int) int { return v1 + v2 })
// Returns map[string]int{"a": 1, "b": 5, "c": 4}

func Reject

func Reject[K comparable, V any](m map[K]V, omit func(key K, val V) bool) map[K]V

Reject returns a new map containing only entries that do NOT satisfy the predicate. Complement of Filter - removes entries where the predicate returns true.

Example:

m := map[string]int{"a": 1, "b": 2, "c": 3, "d": 4}
mapz.Reject(m, func(k string, v int) bool { return v > 2 })
// Returns map[string]int{"a": 1, "b": 2} (removes values > 2)

func RejectByKeys

func RejectByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V

RejectByKeys returns a new map excluding entries with keys in the provided slice.

Example:

m := map[string]int{"a": 1, "b": 2, "c": 3, "d": 4}
mapz.RejectByKeys(m, []string{"b", "d"})
// Returns map[string]int{"a": 1, "c": 3}

func RejectByValues

func RejectByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V

RejectByValues returns a new map excluding entries with values in the provided slice.

Example:

m := map[string]int{"a": 1, "b": 2, "c": 3, "d": 2}
mapz.RejectByValues(m, []int{2, 4})
// Returns map[string]int{"a": 1, "c": 3} (removes entries with value 2)

func Remap

func Remap[K comparable, V any, K2 comparable, V2 any](in map[K]V, mapper func(K, V) (K2, V2)) map[K2]V2

Remap manipulates a map keys and values and transforms it to a map of another types.

func RemapKeys

func RemapKeys[K comparable, V any, K2 comparable](in map[K]V, mapper func(K, V) K2) map[K2]V

RemapKeys manipulates a map keys and transforms it to a map of another types.

func RemapValues

func RemapValues[K comparable, V any, V2 any](in map[K]V, mapper func(K, V) V2) map[K]V2

RemapValues manipulates a map keys and transforms it to a map of another types.

func Slice

func Slice[E any, K comparable, V any](m map[K]V, zip func(K, V) E) []E

Slice converts a map to a slice by applying the zip function to each entry. The order of entries is non-deterministic due to map iteration order.

Example:

m := map[string]int{"a": 1, "b": 2}
mapz.Slice(m, func(k string, v int) string { return k + "=" + strconv.Itoa(v) })
// Might return []string{"a=1", "b=2"} (order not guaranteed)

func Update added in v1.2.0

func Update[K comparable, V any](m map[K]V, key K, updater func(V) V) bool

Update updates the value at the given key if it exists. The updater function receives the current value and returns the new value. Returns true if the key was found and updated, false otherwise.

Example:

m := map[string]int{"counter": 0}
Update(m, "counter", func(v int) int { return v + 1 }) // m is now {"counter": 1}
Update(m, "missing", func(v int) int { return v + 1 }) // no change, returns false

func ValueOr

func ValueOr[K comparable, V any](m map[K]V, key K, fallback V) V

ValueOr returns the value for the given key, or the fallback value if the key doesn't exist.

Example:

m := map[string]int{"a": 1, "b": 2}
mapz.ValueOr(m, "a", 0)       // Returns 1 (key exists)
mapz.ValueOr(m, "c", 999)     // Returns 999 (key doesn't exist)

func Values

func Values[K comparable, V any](m map[K]V) []V

Values returns all values in a map in a none deterministic order

Types

type Entry

type Entry[K comparable, V any] struct {
	Key   K
	Value V
}

Entry represents a single key-value pair from a map. Used for converting between maps and slices of entries.

func Entries

func Entries[K comparable, V any](m map[K]V) []Entry[K, V]

Entries converts a map to a slice of Entry structs. The order of entries is non-deterministic due to map iteration order.

Example:

m := map[string]int{"a": 1, "b": 2}
entries := mapz.Entries(m)
// Might return []Entry{{"a", 1}, {"b", 2}} (order not guaranteed)

Jump to

Keyboard shortcuts

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