from_pairs fn
fn from_pairs[K, V](pairs: List[(K, V)]) -> Map[K, V]The map of pairs; a later pair for a key replaces an earlier one, as in a series of puts.
Package
Map utilities (merge with resolvers, invert, filter and map keys and values, counters, multimaps, diffs) and dynamic JSON values with paths, pointers and merge patches
0.1.1 MIT
sspur add maps@^0.1.1or in sspur.toml:
maps = { version = "^0.1.1" }Utilities for SSPUR's builtin Map[K, V] that the standard library leaves out (merging with a resolver, inverting, filtering and mapping keys and values, one-key updates, counters, multimaps and diffs), and dynamic JSON values: the standard library's json.decode[T] needs a static type, so maps adds JsonValue with a strict RFC 8259 parser, a serializer, dotted paths, RFC 6901 JSON Pointers, deep get/set/delete, deep merge, RFC 7386 merge patches, flattening and deep diffs.
Everything is pure. Functions that take a lambda are effect-polymorphic: they perform what the lambda performs. Maps are persistent values, so every function returns a new map.
use maps.{counter, most_common, merge_with, invert_multi, diff_maps, parse_json, error_text, get_path, set_path, merge_patch, render, render_pretty, JsonValue, JStr}
fn main() -> Unit ! log
= do
words = "the cat and the hat and the bat".split(" ")
log(most_common(counter(words), 2).str)
stock = merge_with(maps.from_pairs([("apples", 3), ("pears", 1)]), maps.from_pairs([("apples", 2), ("plums", 5)]), (k, a, b) => a + b)
log(stock.str)
log(invert_multi(maps.from_pairs([("ana", "admin"), ("bo", "dev"), ("cy", "admin")])).str)
log(diff_maps(stock, stock.put("pears", 0).remove("plums")).str)
match parse_json("\{\"user\": \{\"name\": \"Ana\", \"tags\": [\"a\", \"b\"]}, \"n\": 12345678901234567890}")
| err(e) => log(error_text(e))
| ok(doc) => do
log(get_path(doc, "user.tags[1]").str)
updated = set_path(doc, "user.address.city", JStr{s: "Pune"}).or(doc)
patched = merge_patch(updated, parse_json("\{\"n\": null, \"user\": \{\"tags\": [\"z\"]}}").or(maps.JNull))
log(render(patched))
log(render_pretty(patched, 2))
match parse_json("[1, 2,]")
| err(e) => log(error_text(e))
| ok(_) => log("parsed")
Output (the same natively and with --interp):
[("the", 3), ("and", 2)]
{"apples": 5, "pears": 1, "plums": 5}
{"admin": ["ana", "cy"], "dev": ["bo"]}
MapDiff{added: {}, removed: {"plums": 5}, changed: {"pears": (1, 0)}}
some(JStr{s: "b"})
{"user":{"address":{"city":"Pune"},"name":"Ana","tags":["z"]}}
{
"user": {
"address": {
"city": "Pune"
},
"name": "Ana",
"tags": [
"z"
]
}
}
line 1, column 7: trailing comma before ']'
| Function | What it does |
|---|---|
from_pairs(pairs) | The map of pairs; a later pair for a key wins |
from_pairs_with(pairs, resolve) | Repeated keys combined with resolve(key, existing, incoming) |
from_keys(keys, f) | Each key mapped to f(key) |
from_lists(keys, values) | Keys zipped with values; extras ignored |
index_by(xs, key) | Each element under key(x); a later element wins |
group_into(xs, key) | Elements grouped by key(x) into a multimap, in input order |
to_hash(m), from_hash(h) | Convert to and from HashMap |
| Function | What it does |
|---|---|
get_or(m, k, default) | The value, or the default |
is_empty_map(m) | Whether the map has no entries (Map has no builtin is_empty) |
find_key(m, v) | The smallest key whose value is v |
find_entry(m, f) | The first entry, in key order, with f(k, v) |
keys_where(m, f) | The keys whose value satisfies f |
has_all(m, ks), has_any(m, ks) | Whether all, or any, of the keys are present |
min_by_value(m), max_by_value(m) | The entry with the smallest or largest value (smallest key on ties) |
| Function | What it does |
|---|---|
merge(a, b) | Both maps, b winning (Python's a | b) |
merge_prefer_left(a, b) | Both maps, a winning (lodash defaults) |
merge_with(a, b, resolve) | Shared keys combined with resolve(key, from_a, from_b) |
merge_all(ms), merge_all_with(ms, resolve) | A list of maps, left to right |
intersect_keys(a, b), subtract_keys(a, b) | Entries of a whose key is, or is not, in b |
| Function | What it does |
|---|---|
invert(m) | Value to key; on a shared value the largest key wins |
invert_with(m, resolve) | Value to key, combining keys with resolve(value, existing, next) |
invert_multi(m) | Value to all its keys, ascending |
| Function | What it does |
|---|---|
filter_keys, filter_values, filter_entries | Keep the entries that pass |
partition_entries(m, f) | The entries that pass and the ones that don't |
map_values(m, f), map_values_with_key(m, f) | New values |
map_keys(m, f), map_keys_with(m, f, resolve) | New keys; collisions keep the later original key, or are resolved |
map_entries(m, f) | New pairs; a collision keeps the later entry |
filter_map_values(m, f) | Keep and transform in one pass (f gives Opt) |
pick(m, ks), omit(m, ks) | Only, or all but, the given keys |
rename_key(m, from, to) | Move one entry to a new key |
| Function | What it does |
|---|---|
adjust(m, k, f) | f(v) at k if present |
update(m, k, f) | f(v) at k if present; none removes it |
upsert(m, k, init, f) | f(v) if present, else init (Java's Map.merge) |
alter(m, k, f) | f(current) decides: store or remove (Java's compute) |
put_if_absent(m, k, v) | Store only when missing |
| Function | What it does |
|---|---|
counter(xs) | How many times each element occurs |
increment(m, k, by) | Add to one count (missing counts as 0) |
add_counts(a, b), subtract_counts(a, b) | Counter arithmetic (subtraction keeps positive counts) |
most_common(m, n), least_common(m, n) | The top or bottom n, ties in key order |
count_total(m) | The sum of the counts |
Map[K, List[V]])| Function | What it does |
|---|---|
multi_add(m, k, v), multi_get(m, k) | Append a value; the values at a key ([] when missing) |
multi_remove(m, k, v) | Remove every occurrence; an empty key goes |
multi_from_pairs(pairs), multi_pairs(m) | Build from pairs; flatten to pairs |
multi_size(m) | The number of values over all keys |
| Definition | What it does |
|---|---|
same_keys(a, b), equal_by(a, b, eq), is_submap(a, b) | Key and value comparisons |
MapDiff[K, V] | added, removed, and changed as (old, new) |
diff_maps(old, new_map), diff_is_empty(d), apply_diff(m, d) | Compute, test and apply a diff; apply_diff(a, diff_maps(a, b)) == b |
| Definition | What it does |
|---|---|
JsonValue | JNull, JBool{b}, JInt{i}, JNum{f}, JBig{digits}, JStr{s}, JArr{elems}, JObj{fields} (a Map[Str, JsonValue], so keys are sorted) |
obj(pairs) | An object from pairs |
kind_of, is_null, as_bool, as_int, as_f64, as_str, as_array, as_object | Inspect a value |
JsonError, parse_json(text), parse_json_depth(text, max), default_max_depth(), error_text(e) | Strict parsing with line and column |
render(v), render_pretty(v, indent), quote_json(s) | Serializing |
Numbers: an integer that fits Int is JInt; a number with a fraction or exponent is JNum; a number that fits neither (an integer beyond 64 bits, or 1e400) is JBig with its exact text, so nothing is silently rounded or lost. -0 is JInt{i: 0} and -0.0 is JNum{f: -0.0}. A non-finite JNum renders as null. A repeated key in an object keeps its last value.
| Definition | What it does |
|---|---|
PathSeg | Key{key} or At{pos} |
parse_path(p), path_text(segs) | Dotted paths: a.b[0].c, ["x.y"] for odd keys |
parse_pointer(p), pointer_text(segs) | RFC 6901 pointers: /a/b/0, ~0 and ~1 escapes |
get_in, has_in, set_in, delete_in, update_in | Deep access by steps |
get_path, has_path, set_path, delete_path | The same with a dotted path |
get_pointer, has_pointer, set_pointer, delete_pointer | The same with a JSON Pointer |
set_in creates missing objects (and arrays for an At step), replaces a null on the way, appends at the array's length (or with the pointer step -), and refuses to leave holes in arrays or to step into a scalar, with an error naming the path.
| Definition | What it does |
|---|---|
deep_merge(a, b) | Objects merge recursively, everything else from b |
merge_patch(target, patch), make_merge_patch(source, target) | RFC 7386 JSON Merge Patch, applied and generated |
flatten_value(v), unflatten_value(m) | To and from a map of dotted paths to leaves |
Change, diff_values(a, b), apply_changes(v, changes) | Deep diffs (Added, Removed, Replaced with paths), and applying them |
HashMap versions of every function: convert with to_hash and from_hash, or use the same builtin methods (get put remove items), which both map types share.FlatMap has lower_bound and between.Maps, sorted by key, as json.encode writes Map[Str, V]. Converting between JsonValue and typed records: use parse_json(json.encode(x)) and json.decode[T](render(v)).diff_values compares arrays position by position.sspur add maps
MIT licensed; see LICENSE.
fn from_pairs[K, V](pairs: List[(K, V)]) -> Map[K, V]The map of pairs; a later pair for a key replaces an earlier one, as in a series of puts.
fn from_pairs_with[K, V, e](pairs: List[(K, V)], resolve: (K, V, V) -> V ! e) -> Map[K, V] ! eThe map of pairs, combining the values of a repeated key with resolve(key, existing, incoming).
fn from_keys[K, V, e](keys: List[K], f: K -> V ! e) -> Map[K, V] ! eThe map from each of keys to f(key).
fn from_lists[K, V](keys: List[K], values: List[V]) -> Map[K, V]The map from keys[i] to values[i]; extra keys or values are ignored, and a repeated key takes its last value.
fn index_by[T, K, e](xs: List[T], key: T -> K ! e) -> Map[K, T] ! eThe map from key(x) to x for each element; a later element with the same key wins.
fn group_into[T, K, e](xs: List[T], key: T -> K ! e) -> Map[K, List[T]] ! eThe elements of xs grouped by key(x), each group in the order of xs.
fn to_hash[K, V](m: Map[K, V]) -> HashMap[K, V]The same entries as a HashMap.
fn from_hash[K, V](h: HashMap[K, V]) -> Map[K, V]The same entries as an ordered Map.
fn get_or[K, V](m: Map[K, V], k: K, default: V) -> VThe value for k, or default when k is missing.
fn is_empty_map[K, V](m: Map[K, V]) -> BoolWhether m has no entries.
fn find_key[K, V](m: Map[K, V], v: V) -> Opt[K]The smallest key whose value equals v.
fn find_entry[K, V, e](m: Map[K, V], f: (K, V) -> Bool ! e) -> Opt[(K, V)] ! eThe first entry, in key order, for which f(key, value) holds.
fn keys_where[K, V, e](m: Map[K, V], f: V -> Bool ! e) -> List[K] ! eThe keys, in order, whose value satisfies f.
fn has_all[K, V](m: Map[K, V], ks: List[K]) -> BoolWhether every key of ks is in m.
fn has_any[K, V](m: Map[K, V], ks: List[K]) -> BoolWhether some key of ks is in m.
fn min_by_value[K, V: Ord](m: Map[K, V]) -> Opt[(K, V)]The entry with the smallest value (the smallest key among equal values).
fn max_by_value[K, V: Ord](m: Map[K, V]) -> Opt[(K, V)]The entry with the largest value (the smallest key among equal values).
fn merge[K, V](a: Map[K, V], b: Map[K, V]) -> Map[K, V]The entries of both maps; for a key in both, the value from b wins (like Python's a | b).
fn merge_prefer_left[K, V](a: Map[K, V], b: Map[K, V]) -> Map[K, V]The entries of both maps; for a key in both, the value from a wins (lodash defaults).
fn merge_with[K, V, e](a: Map[K, V], b: Map[K, V], resolve: (K, V, V) -> V ! e) -> Map[K, V] ! eThe entries of both maps; for a key in both, the value is resolve(key, from_a, from_b).
fn merge_all[K, V](ms: List[Map[K, V]]) -> Map[K, V]The entries of all maps, later maps winning; the empty map for an empty list.
fn merge_all_with[K, V, e](ms: List[Map[K, V]], resolve: (K, V, V) -> V ! e) -> Map[K, V] ! eThe entries of all maps, folding repeated keys from left to right with resolve(key, acc, next).
fn intersect_keys[K, V, W](a: Map[K, V], b: Map[K, W]) -> Map[K, V]The entries of a whose key is also in b, with a's values.
fn subtract_keys[K, V, W](a: Map[K, V], b: Map[K, W]) -> Map[K, V]The entries of a whose key is not in b.
fn invert[K, V](m: Map[K, V]) -> Map[V, K]The map from each value to its key. When several keys share a value the largest key wins, because entries are visited in key order and later ones replace earlier ones.
fn invert_with[K, V, e](m: Map[K, V], resolve: (V, K, K) -> K ! e) -> Map[V, K] ! eThe map from each value to its key, combining keys that share a value with resolve(value, existing_key, next_key) (keys arrive in ascending order).
fn invert_multi[K, V](m: Map[K, V]) -> Map[V, List[K]]The map from each value to all keys that have it, in ascending key order.
fn filter_keys[K, V, e](m: Map[K, V], f: K -> Bool ! e) -> Map[K, V] ! eThe entries whose key satisfies f.
fn filter_values[K, V, e](m: Map[K, V], f: V -> Bool ! e) -> Map[K, V] ! eThe entries whose value satisfies f.
fn filter_entries[K, V, e](m: Map[K, V], f: (K, V) -> Bool ! e) -> Map[K, V] ! eThe entries for which f(key, value) holds.
fn partition_entries[K, V, e](m: Map[K, V], f: (K, V) -> Bool ! e) -> (Map[K, V], Map[K, V]) ! eThe entries that satisfy f(key, value) and the ones that don't.
fn map_values[K, V, W, e](m: Map[K, V], f: V -> W ! e) -> Map[K, W] ! eEvery value replaced by f(value).
fn map_values_with_key[K, V, W, e](m: Map[K, V], f: (K, V) -> W ! e) -> Map[K, W] ! eEvery value replaced by f(key, value).
fn map_keys[K, V, K2, e](m: Map[K, V], f: K -> K2 ! e) -> Map[K2, V] ! eEvery key replaced by f(key). When two keys map to the same new key, the value of the larger original key wins; use map_keys_with to choose.
fn map_keys_with[K, V, K2, e](m: Map[K, V], f: K -> K2 ! e, resolve: (K2, V, V) -> V ! e) -> Map[K2, V] ! eEvery key replaced by f(key), combining colliding values with resolve(new_key, existing, incoming) in ascending order of the original keys.
fn map_entries[K, V, K2, W, e](m: Map[K, V], f: (K, V) -> (K2, W) ! e) -> Map[K2, W] ! eEvery entry replaced by the pair f(key, value); on a key collision the later entry, in the original key order, wins.
fn filter_map_values[K, V, W, e](m: Map[K, V], f: (K, V) -> Opt[W] ! e) -> Map[K, W] ! eThe entries for which f(key, value) gives some(w), with the value w.
fn pick[K, V](m: Map[K, V], ks: List[K]) -> Map[K, V]Only the entries whose key is in ks.
fn omit[K, V](m: Map[K, V], ks: List[K]) -> Map[K, V]The entries whose key is not in ks.
fn rename_key[K, V](m: Map[K, V], from: K, to: K) -> Map[K, V]The map with the entry of from moved to the key to, replacing any entry already at to. Unchanged when from is missing or equal to to.
fn adjust[K, V, e](m: Map[K, V], k: K, f: V -> V ! e) -> Map[K, V] ! eThe value at k replaced by f(value); unchanged when k is missing.
fn update[K, V, e](m: Map[K, V], k: K, f: V -> Opt[V] ! e) -> Map[K, V] ! eThe value at k replaced by f(value), or removed when f gives none; unchanged when k is missing.
fn upsert[K, V, e](m: Map[K, V], k: K, init: V, f: V -> V ! e) -> Map[K, V] ! ef(value) at k when k is present, else init (Java's Map.merge).
fn alter[K, V, e](m: Map[K, V], k: K, f: Opt[V] -> Opt[V] ! e) -> Map[K, V] ! eThe entry at k decided by f(current): some(v) stores v, none removes it (Haskell's alter, Java's compute).
fn put_if_absent[K, V](m: Map[K, V], k: K, v: V) -> Map[K, V]The map with v at k only when k is missing (Python's setdefault, without the read).
fn counter[T](xs: List[T]) -> Map[T, Int]How many times each element occurs.
fn increment[K](m: Map[K, Int], k: K, by: Int) -> Map[K, Int]The count at k raised by by (which may be negative); a missing key counts as 0, and a key whose count reaches 0 stays in the map.
fn add_counts[K](a: Map[K, Int], b: Map[K, Int]) -> Map[K, Int]The counts of both counters added key by key.
fn subtract_counts[K](a: Map[K, Int], b: Map[K, Int]) -> Map[K, Int]The counts of a minus those of b, keeping only keys whose result is positive (Python's Counter - Counter).
fn most_common[K](m: Map[K, Int], n: Int where _ >= 0) -> List[(K, Int)]The n entries with the highest counts, highest first; equal counts are in key order. Fewer when the counter is smaller; n 0 gives [].
fn least_common[K](m: Map[K, Int], n: Int where _ >= 0) -> List[(K, Int)]The n entries with the lowest counts, lowest first; equal counts are in key order.
fn count_total[K](m: Map[K, Int]) -> IntThe sum of all counts.
fn multi_add[K, V](m: Map[K, List[V]], k: K, v: V) -> Map[K, List[V]]The multimap with v appended to the list at k.
fn multi_get[K, V](m: Map[K, List[V]], k: K) -> List[V]The values at k, [] when missing.
fn multi_remove[K, V](m: Map[K, List[V]], k: K, v: V) -> Map[K, List[V]]The multimap with every occurrence of v removed from the list at k; the key goes when its list becomes empty.
fn multi_from_pairs[K, V](pairs: List[(K, V)]) -> Map[K, List[V]]The multimap of pairs, each key's values in the order given.
fn multi_pairs[K, V](m: Map[K, List[V]]) -> List[(K, V)]Every (key, value) pair of the multimap, in key order and then list order.
fn multi_size[K, V](m: Map[K, List[V]]) -> IntThe number of values in the multimap, over all keys.
fn same_keys[K, V, W](a: Map[K, V], b: Map[K, W]) -> BoolWhether both maps have exactly the same keys.
fn equal_by[K, V, W, e](a: Map[K, V], b: Map[K, W], eq: (V, W) -> Bool ! e) -> Bool ! eWhether both maps have the same keys and eq holds for the values at each key.
fn is_submap[K, V](a: Map[K, V], b: Map[K, V]) -> BoolWhether every entry of a is also in b with an equal value.
pub type MapDiff[K, V] = {added: Map[K, V], removed: Map[K, V], changed: Map[K, (V, V)]} derive Eq, ShowThe difference between two maps: entries only in the new map, entries only in the old one, and keys in both whose values differ, as (old, new).
fn diff_maps[K, V](old: Map[K, V], new_map: Map[K, V]) -> MapDiff[K, V]What changed from old to new_map.
fn diff_is_empty[K, V](d: MapDiff[K, V]) -> BoolWhether a diff has no changes.
fn apply_diff[K, V](m: Map[K, V], d: MapDiff[K, V]) -> Map[K, V]m with a diff applied: removed keys go, added and changed keys take their new values. So apply_diff(a, diff_maps(a, b)) == b. Keys of removed and changed are applied whatever their current value is.
pub type JsonValue = JNull | JBool{b: Bool} | JInt{i: Int} | JNum{f: F64} | JBig{digits: Str} | JStr{s: Str} | JArr{elems: List[JsonValue]} | JObj{fields: Map[Str, JsonValue]} derive Eq, ShowA JSON value. Integers that fit Int are JInt; numbers with a fraction or an exponent are JNum; a number that fits neither (an integer beyond 64 bits, or a float that overflows) is JBig, which keeps its exact text. Object keys are kept sorted (a Map), so objects compare and print the same whatever order their keys were written in. The functions below recurse on nesting and are tested to 4096 levels in both tiers.
pub type JsonError = {message: Str, offset: Int, line: Int, column: Int} derive Eq, ShowWhere and why JSON text failed to parse: offset counts characters from 0, line and column count from 1.
pub type PathSeg = Key{key: Str} | At{pos: Int where _ >= 0} derive Eq, Ord, ShowOne step of a path: an object key, or an array position.
pub type Change = Added{path: List[PathSeg], value: JsonValue} | Removed{path: List[PathSeg], value: JsonValue} | Replaced{path: List[PathSeg], before: JsonValue, after: JsonValue} derive Eq, ShowA difference found by diff_values, at a path from the root.
fn default_max_depth() -> IntThe deepest nesting of arrays and objects parse_json accepts.
fn obj(pairs: List[(Str, JsonValue)]) -> JsonValueAn object from key-value pairs; a repeated key takes its last value.
fn kind_of(v: JsonValue) -> StrThe kind of a value: "null", "boolean", "number" (JInt, JNum and JBig), "string", "array" or "object".
fn is_null(v: JsonValue) -> BoolWhether the value is null.
fn as_bool(v: JsonValue) -> Opt[Bool]The boolean, if the value is one.
fn as_int(v: JsonValue) -> Opt[Int]The integer, if the value is a JInt, or a JNum with no fraction within the Int range.
fn as_f64(v: JsonValue) -> Opt[F64]The number as an F64, for any number; a JBig integer is rounded and an overflowing one is none.
fn as_str(v: JsonValue) -> Opt[Str]The string, if the value is one.
fn as_array(v: JsonValue) -> Opt[List[JsonValue]]The elements, if the value is an array.
fn as_object(v: JsonValue) -> Opt[Map[Str, JsonValue]]The fields, if the value is an object.
fn parse_json(text: Str) -> Res[JsonValue, JsonError]The value of JSON text, strictly as RFC 8259 says: one value with optional whitespace around it, no comments, no trailing commas, no single quotes, no leading zeros or +, no NaN, and strings with valid escapes and no raw control characters; a \u escape of a lone surrogate is an error. A repeated object key keeps its last value. Nesting deeper than default_max_depth() is an error.
fn parse_json_depth(text: Str, max_depth: Int where _ >= 0 and _ <= 4096) -> Res[JsonValue, JsonError]parse_json with a nesting limit: arrays and objects may nest max_depth deep (0 allows only scalars).
fn error_text(e: JsonError) -> StrThe error as one line: "line 1, column 5: expected ':' after an object key".
fn render(v: JsonValue) -> StrThe value as compact JSON text: no spaces, object keys in sorted order, strings escaped as RFC 8259 requires (", \, and control characters; other characters are written as they are). A JNum that is not finite is written null; a JBig is written as its text.
fn render_pretty(v: JsonValue, indent: Int where _ >= 0 and _ <= 16) -> StrThe value as indented JSON text: one element or field per line, indent spaces per level, ": " after keys, and [] and {} for empty containers. indent 0 still breaks lines.
fn quote_json(s: Str) -> StrThe string as a JSON string literal, with its quotes.
fn parse_path(path: Str) -> Res[List[PathSeg], Str]The steps of a dotted path such as a.b[0].c or users[2]["first name"]: names separated by dots, [n] for an array position, and ["..."] (with \" and \\ escapes) for a key that has dots, brackets or quotes in it. "" is the root (no steps). A name can't be empty (a..b is an error) and a position can't be negative or have a leading zero.
fn path_text(segs: List[PathSeg]) -> StrA path as text that parse_path reads back: plain names joined with dots, [n] for positions, and ["..."] for empty keys and keys with ., [, ] or ".
fn parse_pointer(ptr: Str) -> Res[List[PathSeg], Str]The steps of an RFC 6901 JSON Pointer such as /a/b/0: "" is the whole document, every other pointer starts with /, and ~1 and ~0 in a step stand for / and ~. Every step is a Key; on an array a key that is a position (0, 12, not 01) indexes it, and - means the end (for set_in).
fn pointer_text(segs: List[PathSeg]) -> StrA path as an RFC 6901 JSON Pointer, escaping ~ as ~0 and / as ~1.
fn get_in(v: JsonValue, segs: List[PathSeg]) -> Opt[JsonValue]The value at a path, or none when a step is missing. A Key step reads an object field, or an array element when the key is a position; an At step reads only array elements.
fn has_in(v: JsonValue, segs: List[PathSeg]) -> BoolWhether there is a value at the path (a null there counts).
fn set_in(v: JsonValue, segs: List[PathSeg], x: JsonValue) -> Res[JsonValue, Str]The value with x stored at a path, or why it can't be. Missing objects along the way are created (and a null is replaced by one), as an object for a Key step and an array for an At step. On an array the position must be at most its length (the length, or the key -, appends), so arrays never get holes. A step into a boolean, number or string, a key that is not a position on an array, or an At step on an object is an error that names the path.
fn delete_in(v: JsonValue, segs: List[PathSeg]) -> JsonValueThe value without whatever is at a path: an object field is removed and an array element is taken out (later elements move down). Unchanged when the path is missing or segs is empty.
fn update_in[e](v: JsonValue, segs: List[PathSeg], f: JsonValue -> JsonValue ! e) -> JsonValue ! eThe value with the value at a path replaced by f(old); unchanged when the path is missing.
fn get_path(v: JsonValue, path: Str) -> Opt[JsonValue]get_in with a dotted path; none also when the path doesn't parse.
fn has_path(v: JsonValue, path: Str) -> Boolhas_in with a dotted path; false also when the path doesn't parse.
fn set_path(v: JsonValue, path: Str, x: JsonValue) -> Res[JsonValue, Str]set_in with a dotted path; a path that doesn't parse is an error too.
fn delete_path(v: JsonValue, path: Str) -> Res[JsonValue, Str]delete_in with a dotted path; a path that doesn't parse is an error.
fn get_pointer(v: JsonValue, ptr: Str) -> Opt[JsonValue]get_in with a JSON Pointer; none also when the pointer doesn't parse.
fn has_pointer(v: JsonValue, ptr: Str) -> Boolhas_in with a JSON Pointer.
fn set_pointer(v: JsonValue, ptr: Str, x: JsonValue) -> Res[JsonValue, Str]set_in with a JSON Pointer; - as an array step appends.
fn delete_pointer(v: JsonValue, ptr: Str) -> Res[JsonValue, Str]delete_in with a JSON Pointer.
fn deep_merge(a: JsonValue, b: JsonValue) -> JsonValueb merged into a: where both are objects their fields merge recursively, anywhere else (arrays included) the value from b replaces the one from a.
fn merge_patch(target: JsonValue, patch: JsonValue) -> JsonValuetarget with an RFC 7386 JSON Merge Patch applied: an object patch sets each of its fields recursively and removes the fields it sets to null; any other patch replaces the target.
fn make_merge_patch(source: JsonValue, target: JsonValue) -> JsonValueThe RFC 7386 merge patch that turns source into target, so merge_patch(source, make_merge_patch(source, target)) == target whenever target has no null inside an object (merge patches can't set a field to null). Unchanged fields are left out; arrays are replaced whole.
fn flatten_value(v: JsonValue) -> Map[Str, JsonValue]The leaves of a value keyed by their path_text: scalars, and empty arrays and objects (so unflatten_value can restore them). A scalar or empty root is the single key "".
fn unflatten_value(m: Map[Str, JsonValue]) -> Res[JsonValue, Str]The value whose leaves are m, the inverse of flatten_value: each key is parsed with parse_path and set with set_in, in path order (an empty map gives an empty object). Errors when a key doesn't parse, when two keys conflict (a and a.b), or when array positions skip one.
fn diff_values(a: JsonValue, b: JsonValue) -> List[Change]The differences from a to b, in path order: fields and trailing array elements only in b are Added, only in a Removed, and other values that differ Replaced (a change of kind, or of a scalar). Arrays are compared position by position, so an insertion shows as replacements plus an addition. Equal values give [].
fn apply_changes(v: JsonValue, changes: List[Change]) -> Res[JsonValue, Str]v with changes applied, as diff_values produces them: Replaced and Added in order, then Removed from the last to the first so array positions stay valid. So apply_changes(a, diff_values(a, b)) == ok(b). Errors when a path can't be set.
| Version | Published | Commit | Source | Effects |
|---|---|---|---|---|
| 0.1.1 | 2026-10-18 | 45f6af9963 | snapshot | |
| 0.1.0 | 2026-10-16 | a832998d6f | snapshot |