object

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jul 18, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package object ports the "Object" category of the npm lodash library to Go, providing the map- and structure-oriented helpers such as Keys, Values, Entries, Pick, Omit, MapKeys, MapValues, Invert, Assign, Merge, Defaults, Get, Set, Has, Unset, Update, Clone, CloneDeep, IsEqual and Transform. In JavaScript these operate on plain objects; here they operate on Go maps and, for the dynamically-typed path and merge helpers, on the any-typed tree of map[string]any and []any values that a decoded JSON document produces. The package depends only on the Go standard library.

Reach for this package when you are manipulating decoded JSON or other loosely-typed nested data and want the ergonomic, batteries-included vocabulary that lodash offers on the front end: extracting a subset of keys, re-keying or re-mapping values, deeply merging configuration layers, reading or writing a value several levels down by a single "a.b.c" path, or comparing two trees for structural equality. The statically-typed helpers (Keys, Values, Entries, Pick, Omit, MapKeys, MapValues, Invert, FindKey and friends) are written with Go generics so they work over any comparable key type and any value type without reflection or boxing.

The functions divide into two families by how they are typed. The collection helpers are generic over [K comparable, V any] and simply iterate the map to build a fresh result; Pick copies only the requested keys that are present, Omit copies everything except a drop-set of keys, and the *By variants take a predicate or iteratee instead of an explicit key list. The dynamic helpers operate on any: Get, Set, Has, Unset and Update accept dot-notation paths such as "a.b.c", splitting on "." and walking the tree one segment at a time. A numeric segment indexes into a []any slice, so the path "a.0.b" descends into the first element of the slice stored under key "a"; Set creates the intermediate map or slice containers on demand (choosing a slice when the next segment is numeric) and grows slices with nil padding as needed.

Edge-case semantics follow lodash closely. Get returns the supplied defaultValue (or nil) whenever any segment along the path is missing, out of range, or the resolved value is itself nil. Defaults and DefaultsDeep only fill keys that are absent or hold nil, never overwriting an existing non-nil value, and DefaultsDeep recurses into nested maps while deep-cloning the values it copies in. Merge recursively merges nested maps key by key and nested slices index by index, skipping nil source values, whereas Assign is a shallow overwrite. CloneDeep recursively copies map and slice containers so that later mutation cannot reach the original, and IsEqual compares trees structurally, guarding against panics from uncomparable scalar types by treating them as unequal. Passing a nil destination to the mutating helpers (Assign, Merge, Defaults, DefaultsDeep, Transform) allocates a fresh map.

Parity with Node's lodash is close in behavior but adapted to Go's type system and runtime. The most visible difference is ordering: Go map iteration is randomized, so Keys, Values, Entries, MapKeys and the For* iterators do not preserve insertion order the way lodash does, and functions whose result would otherwise be order-dependent take deliberate steps to stay deterministic (FindKey examines keys in sorted order). There is no notion of JavaScript "undefined" versus a present-but-null value, so nil stands in for both; Unset deletes a map key but, matching lodash's array delete, only nils out a slice element rather than shifting the slice. Symbol keys, prototype chains, getters and lodash's customizer callbacks have no equivalent here.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Assign

func Assign(dst map[string]any, sources ...map[string]any) map[string]any

Assign copies the own enumerable keys of each source into dst, with later sources overwriting earlier ones. It mutates and returns dst. This is a shallow copy (lodash's assign / Object.assign).

func AssignWith added in v0.4.0

func AssignWith[K comparable, V any](dst, src map[K]V, customizer func(dstVal, srcVal V, key K) V) map[K]V

AssignWith merges src into a copy of dst, resolving each key with customizer, which receives the destination and source values (the destination value is the zero value of V when the key is only in src) and returns the value to store. It mirrors lodash.assignWith and never mutates its arguments.

func At added in v0.4.0

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

At returns the values of m at the given keys, in the order the keys are supplied. A key that is not present yields the zero value of V. It mirrors lodash.at for a map.

func Clone

func Clone(value any) any

Clone returns a shallow copy of value. For maps and slices a new top-level container is allocated but nested values are shared with the original. Scalar values are returned unchanged.

func CloneDeep

func CloneDeep(value any) any

CloneDeep returns a deep copy of value, recursively copying nested map[string]any and []any containers so that mutating the result never affects the original. Scalar values are returned unchanged.

func Defaults

func Defaults(dst map[string]any, sources ...map[string]any) map[string]any

Defaults assigns, for each source in sources, the values of keys that are missing (or hold the zero value equivalent of "undefined") in dst. Existing, non-nil keys in dst are preserved. It mutates and returns dst.

Faithful to lodash, only the first value encountered for a key is used, and keys already present with a non-nil value are left untouched.

func DefaultsDeep

func DefaultsDeep(dst map[string]any, sources ...map[string]any) map[string]any

DefaultsDeep is like Defaults except it recurses into nested map[string]any values, filling in missing keys at every depth. It mutates and returns dst.

func EveryEntry added in v0.4.0

func EveryEntry[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) bool

EveryEntry reports whether predicate returns true for every entry of m. It returns true for an empty map.

func FindEntry added in v0.4.0

func FindEntry[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) (K, V, bool)

FindEntry returns an arbitrary entry of m for which predicate is true, along with true; if no entry matches it returns zero values and false.

func FindKey

func FindKey[V any](m map[string]V, predicate func(value V, key string) bool) (string, bool)

FindKey returns the key of the first entry of m for which predicate returns true, along with true. If no entry matches, it returns the zero key and false.

Because Go map iteration order is randomized, callers that need a deterministic result over multiple matches should not rely on which matching key is returned. To keep FindKey predictable in the common case, keys are examined in sorted order.

Example

ExampleFindKey returns the key of the first entry satisfying a predicate. Because Go map iteration order is randomized, FindKey examines keys in sorted order to keep its result deterministic when several entries would match. It returns the found key together with true, or the zero key and false when nothing matches. The predicate receives both the value and the key. Here it locates the key whose value equals 2.

package main

import (
	"fmt"

	"github.com/malcolmston/express/lodash/object"
)

func main() {
	m := map[string]int{"a": 1, "b": 2, "c": 3}
	key, ok := object.FindKey(m, func(v int, k string) bool { return v == 2 })
	fmt.Println(key, ok)
}
Output:
b true

func ForIn

func ForIn(m map[string]any, iteratee func(value any, key string) bool)

ForIn iterates over the entries of m, invoking iteratee for each. If iteratee returns false, iteration stops early. (For plain maps ForIn and ForOwn behave identically; both are provided to match lodash.)

func ForOwn

func ForOwn(m map[string]any, iteratee func(value any, key string) bool)

ForOwn iterates over the own enumerable entries of m, invoking iteratee for each. If iteratee returns false, iteration stops early.

func FromEntries

func FromEntries[K comparable, V any](pairs []Pair[K, V]) map[K]V

FromEntries builds a map from a slice of Pair. Later pairs overwrite earlier ones that share a key.

func Get

func Get(root any, path string, defaultValue ...any) any

Get retrieves the value at the given dot-notation path within root. If any segment along the path is missing, the provided defaultValue is returned (or nil if none is supplied). Numeric segments index into []any slices.

Example

ExampleGet demonstrates reading a deeply nested value by a single dot-notation path. The root is the kind of any-typed tree that decoding a JSON document produces, mixing map[string]any and []any containers. A numeric path segment such as "0" indexes into a slice, so "a.b.0.c" walks a map, a slice, and then another map in one call. When any segment along the path is missing or out of range, Get returns the supplied default value instead of panicking. This makes Get a safe way to probe optional configuration values.

package main

import (
	"fmt"

	"github.com/malcolmston/express/lodash/object"
)

func main() {
	root := map[string]any{
		"a": map[string]any{
			"b": []any{
				map[string]any{"c": 42},
			},
		},
	}
	fmt.Println(object.Get(root, "a.b.0.c"))
	fmt.Println(object.Get(root, "a.b.5.c", "missing"))
}
Output:
42
missing

func Has

func Has(root any, path string) bool

Has reports whether root contains a value at the given dot-notation path.

func Invert

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

Invert returns a new map composed of the inverted keys and values of m. If m contains duplicate values, subsequent values overwrite prior ones.

Example

ExampleInvert swaps keys and values, producing a new map whose keys are the original values. Both the key and value types must be comparable so the result is a valid map. When the source contains duplicate values, later entries overwrite earlier ones, so the inverse may be smaller than the original. Here the values are distinct, so the mapping is one-to-one. The output prints in sorted key order.

package main

import (
	"fmt"

	"github.com/malcolmston/express/lodash/object"
)

func main() {
	m := map[string]string{"a": "1", "b": "2"}
	fmt.Println(object.Invert(m))
}
Output:
map[1:a 2:b]

func InvertBy

func InvertBy[K comparable, V any, R comparable](m map[K]V, iteratee func(value V) R) map[R][]K

InvertBy returns a new map composed of keys generated from the results of running each value of m through iteratee. Each key maps to the slice of original keys responsible for generating it.

func IsEqual

func IsEqual(a, b any) bool

IsEqual reports whether a and b are deeply, structurally equal. It compares map[string]any values key by key, []any values element by element, and falls back to == for scalars. Two nil values are equal.

Example

ExampleIsEqual compares two values for deep structural equality. It descends into nested maps and slices, comparing them key by key and element by element, and falls back to == for scalar leaves. Two independently constructed trees with the same shape and contents are reported as equal. This avoids the pitfalls of comparing maps or slices directly, which is not allowed with ==. The comparison is order-independent for maps and order-sensitive for slices.

package main

import (
	"fmt"

	"github.com/malcolmston/express/lodash/object"
)

func main() {
	a := map[string]any{"x": []any{1, 2}}
	b := map[string]any{"x": []any{1, 2}}
	fmt.Println(object.IsEqual(a, b))
}
Output:
true

func Keys

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

Keys returns the keys of m. The order of the returned slice is not guaranteed (Go map iteration order is randomized).

func MapKeys

func MapKeys[K comparable, V any, R comparable](m map[K]V, iteratee func(value V, key K) R) map[R]V

MapKeys returns a new map with the same values as m but with keys produced by running each entry through iteratee. If iteratee maps two keys to the same result, the last one written wins.

func MapValues

func MapValues[K comparable, V any, R any](m map[K]V, iteratee func(value V, key K) R) map[K]R

MapValues returns a new map with the same keys as m but with values produced by running each entry through iteratee.

Example

ExampleMapValues transforms every value in a map while preserving its keys. The iteratee receives both the value and its key and returns the replacement value, which may be of a different type. This is the map-shaped counterpart of a slice transform and does not mutate the source. Here each integer value is doubled. The result prints deterministically in sorted key order.

package main

import (
	"fmt"

	"github.com/malcolmston/express/lodash/object"
)

func main() {
	m := map[string]int{"a": 1, "b": 2}
	doubled := object.MapValues(m, func(v int, k string) int { return v * 2 })
	fmt.Println(doubled)
}
Output:
map[a:2 b:4]

func Merge

func Merge(dst map[string]any, sources ...map[string]any) map[string]any

Merge recursively merges the sources into dst. Nested map[string]any values are merged key by key; nested []any values are merged index by index (as lodash does for array-like objects). Non-mergeable values from a source overwrite the destination unless the source value is nil, in which case the destination is preserved. It mutates and returns dst.

Example

ExampleMerge recursively combines source maps into a destination. Nested maps are merged key by key rather than replaced wholesale, so keys unique to either side survive. This mirrors how lodash deep-merges plain objects and is useful for layering configuration defaults with overrides. Merge mutates and returns the destination map. The nested result prints deterministically because fmt sorts keys at every level.

package main

import (
	"fmt"

	"github.com/malcolmston/express/lodash/object"
)

func main() {
	dst := map[string]any{"a": map[string]any{"x": 1}}
	src := map[string]any{"a": map[string]any{"y": 2}}
	merged := object.Merge(dst, src)
	fmt.Println(merged)
}
Output:
map[a:map[x:1 y:2]]

func Omit

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

Omit returns a new map with the given keys removed from m.

Example

ExampleOmit is the complement of Pick: it copies every entry except the ones whose keys are listed. Like Pick it returns a fresh map and leaves the original untouched. This is handy for stripping sensitive fields such as a password before logging or serializing a record. Listing a key that is not present has no effect. The printed output is deterministic because fmt sorts map keys.

package main

import (
	"fmt"

	"github.com/malcolmston/express/lodash/object"
)

func main() {
	m := map[string]int{"name": 1, "age": 2, "password": 3}
	safe := object.Omit(m, "password")
	fmt.Println(safe)
}
Output:
map[age:2 name:1]

func OmitBy

func OmitBy[K comparable, V any](m map[K]V, predicate func(value V, key K) bool) map[K]V

OmitBy returns a new map with the entries of m for which predicate returns true removed.

func Pick

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

Pick returns a new map composed of the given keys picked from m. Keys that are not present in m are skipped.

Example

ExamplePick builds a new map containing only a chosen subset of keys. It is the map-shaped analogue of selecting columns from a record, and it never mutates the source map. Keys that are requested but not present in the source are simply skipped rather than added with a zero value. Because Go's fmt package prints map keys in sorted order, the output here is deterministic. Pick is generic, so it works over any comparable key type and any value type.

package main

import (
	"fmt"

	"github.com/malcolmston/express/lodash/object"
)

func main() {
	m := map[string]int{"name": 1, "age": 2, "email": 3}
	picked := object.Pick(m, "name", "email")
	fmt.Println(picked)
}
Output:
map[email:3 name:1]

func PickBy

func PickBy[K comparable, V any](m map[K]V, predicate func(value V, key K) bool) map[K]V

PickBy returns a new map composed of the entries of m for which predicate returns true.

func ReduceEntries added in v0.4.0

func ReduceEntries[K comparable, V any, R any](m map[K]V, iteratee func(acc R, key K, value V) R, accumulator R) R

ReduceEntries folds the entries of m into an accumulator using iteratee. Because map iteration order is unspecified, use it only with an associative, commutative reduction (such as summing values). It mirrors lodash.reduce over an object.

func Set

func Set(root any, path string, value any) any

Set sets the value at the given dot-notation path within root, creating intermediate map[string]any containers as needed. Numeric segments index into (and, when necessary, grow) []any slices. It returns the (possibly replaced) root so callers can capture a newly created container.

Example

ExampleSet writes a value at a dot-notation path, creating the intermediate containers as it goes. Passing a nil root lets Set allocate the top-level container for you, choosing a map or a slice based on whether the first segment is numeric. Here the path "a.b.c" is entirely non-numeric, so nested maps are created at each level. Set returns the (possibly newly allocated) root so the caller can capture it. Reading the value back with Get confirms the write landed where expected.

package main

import (
	"fmt"

	"github.com/malcolmston/express/lodash/object"
)

func main() {
	root := object.Set(nil, "a.b.c", 10)
	fmt.Println(object.Get(root, "a.b.c"))
}
Output:
10

func SomeEntry added in v0.4.0

func SomeEntry[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) bool

SomeEntry reports whether predicate returns true for at least one entry of m. It returns false for an empty map.

func Transform

func Transform(m map[string]any, iteratee func(acc map[string]any, value any, key string) bool, accumulator map[string]any) map[string]any

Transform is a variant of Reduce for objects. It runs each entry of m through iteratee, threading an accumulator that iteratee mutates in place. If iteratee returns false, iteration stops early. The accumulator is returned.

func Unset

func Unset(root any, path string) bool

Unset removes the value at the given dot-notation path from root. It reports whether a value was removed. Numeric segments index into []any slices; for slices the element is set to nil (lodash-style delete) rather than removed.

func Update

func Update(root any, path string, updater func(current any) any) any

Update is like Set except the new value is produced by running the current value at path (or nil if absent) through updater. It returns the (possibly replaced) root.

func Values

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

Values returns the values of m. The order of the returned slice is not guaranteed.

Types

type Pair

type Pair[K comparable, V any] struct {
	// Key is the map key of the entry.
	Key K
	// Value is the map value associated with Key.
	Value V
}

Pair is a single key/value entry, as produced by Entries.

func Entries

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

Entries returns the key/value pairs of m as a slice of Pair. It is the generic equivalent of lodash's toPairs.

func ToPairs

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

ToPairs is an alias for Entries, matching lodash's naming.

Jump to

Keyboard shortcuts

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