453 lines
11 KiB
JavaScript
453 lines
11 KiB
JavaScript
import {
|
|
toTransient as to_transient,
|
|
fromTransient as from_transient,
|
|
size,
|
|
fold,
|
|
make as new$,
|
|
destructiveTransientInsert as transient_insert,
|
|
has as has_key,
|
|
get,
|
|
insert,
|
|
map as map_values,
|
|
destructiveTransientUpdateWith as transient_update_with,
|
|
destructiveTransientDelete as transient_delete,
|
|
} from "../dict.mjs";
|
|
import { Ok, toList, Empty as $Empty, prepend as listPrepend } from "../gleam.mjs";
|
|
import * as $option from "../gleam/option.mjs";
|
|
|
|
export { fold, get, has_key, insert, map_values, new$, size };
|
|
|
|
/**
|
|
* Determines whether or not the dict is empty.
|
|
*
|
|
* ## Examples
|
|
*
|
|
* ```gleam
|
|
* assert new() |> is_empty
|
|
* ```
|
|
*
|
|
* ```gleam
|
|
* assert !{ new() |> insert("b", 1) |> is_empty }
|
|
* ```
|
|
*/
|
|
export function is_empty(dict) {
|
|
return size(dict) === 0;
|
|
}
|
|
|
|
/**
|
|
* Converts the dict to a list of 2-element tuples `#(key, value)`, one for
|
|
* each key-value pair in the dict.
|
|
*
|
|
* The tuples in the list have no specific order.
|
|
*
|
|
* ## Examples
|
|
*
|
|
* Calling `to_list` on an empty `dict` returns an empty list.
|
|
*
|
|
* ```gleam
|
|
* assert new() |> to_list == []
|
|
* ```
|
|
*
|
|
* The ordering of elements in the resulting list is an implementation detail
|
|
* that should not be relied upon.
|
|
*
|
|
* ```gleam
|
|
* assert new()
|
|
* |> insert("b", 1)
|
|
* |> insert("a", 0)
|
|
* |> insert("c", 2)
|
|
* |> to_list
|
|
* == [#("a", 0), #("b", 1), #("c", 2)]
|
|
* ```
|
|
*/
|
|
export function to_list(dict) {
|
|
return fold(
|
|
dict,
|
|
toList([]),
|
|
(acc, key, value) => { return listPrepend([key, value], acc); },
|
|
);
|
|
}
|
|
|
|
function from_list_loop(loop$transient, loop$list) {
|
|
while (true) {
|
|
let transient = loop$transient;
|
|
let list = loop$list;
|
|
if (list instanceof $Empty) {
|
|
return from_transient(transient);
|
|
} else {
|
|
let rest = list.tail;
|
|
let key = list.head[0];
|
|
let value = list.head[1];
|
|
loop$transient = transient_insert(key, value, transient);
|
|
loop$list = rest;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converts a list of 2-element tuples `#(key, value)` to a dict.
|
|
*
|
|
* If two tuples have the same key the last one in the list will be the one
|
|
* that is present in the dict.
|
|
*/
|
|
export function from_list(list) {
|
|
return from_list_loop(to_transient(new$()), list);
|
|
}
|
|
|
|
/**
|
|
* Gets a list of all keys in a given dict.
|
|
*
|
|
* Dicts are not ordered so the keys are not returned in any specific order. Do
|
|
* not write code that relies on the order keys are returned by this function
|
|
* as it may change in later versions of Gleam or Erlang.
|
|
*
|
|
* ## Examples
|
|
*
|
|
* ```gleam
|
|
* assert from_list([#("a", 0), #("b", 1)]) |> keys == ["a", "b"]
|
|
* ```
|
|
*/
|
|
export function keys(dict) {
|
|
return fold(
|
|
dict,
|
|
toList([]),
|
|
(acc, key, _) => { return listPrepend(key, acc); },
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Gets a list of all values in a given dict.
|
|
*
|
|
* Dicts are not ordered so the values are not returned in any specific order. Do
|
|
* not write code that relies on the order values are returned by this function
|
|
* as it may change in later versions of Gleam or Erlang.
|
|
*
|
|
* ## Examples
|
|
*
|
|
* ```gleam
|
|
* assert from_list([#("a", 0), #("b", 1)]) |> values == [0, 1]
|
|
* ```
|
|
*/
|
|
export function values(dict) {
|
|
return fold(
|
|
dict,
|
|
toList([]),
|
|
(acc, _, value) => { return listPrepend(value, acc); },
|
|
);
|
|
}
|
|
|
|
function do_filter(f, dict) {
|
|
let _pipe = to_transient(new$());
|
|
let _pipe$1 = fold(
|
|
dict,
|
|
_pipe,
|
|
(transient, key, value) => {
|
|
let $ = f(key, value);
|
|
if ($) {
|
|
return transient_insert(key, value, transient);
|
|
} else {
|
|
return transient;
|
|
}
|
|
},
|
|
);
|
|
return from_transient(_pipe$1);
|
|
}
|
|
|
|
/**
|
|
* Creates a new dict from a given dict, minus any entries that a given function
|
|
* returns `False` for.
|
|
*
|
|
* ## Examples
|
|
*
|
|
* ```gleam
|
|
* assert from_list([#("a", 0), #("b", 1)])
|
|
* |> filter(fn(key, value) { value != 0 })
|
|
* == from_list([#("b", 1)])
|
|
* ```
|
|
*
|
|
* ```gleam
|
|
* assert from_list([#("a", 0), #("b", 1)])
|
|
* |> filter(fn(key, value) { True })
|
|
* == from_list([#("a", 0), #("b", 1)])
|
|
* ```
|
|
*/
|
|
export function filter(dict, predicate) {
|
|
return do_filter(predicate, dict);
|
|
}
|
|
|
|
function do_take_loop(loop$dict, loop$desired_keys, loop$acc) {
|
|
while (true) {
|
|
let dict = loop$dict;
|
|
let desired_keys = loop$desired_keys;
|
|
let acc = loop$acc;
|
|
if (desired_keys instanceof $Empty) {
|
|
return from_transient(acc);
|
|
} else {
|
|
let key = desired_keys.head;
|
|
let rest = desired_keys.tail;
|
|
let $ = get(dict, key);
|
|
if ($ instanceof Ok) {
|
|
let value = $[0];
|
|
loop$dict = dict;
|
|
loop$desired_keys = rest;
|
|
loop$acc = transient_insert(key, value, acc);
|
|
} else {
|
|
loop$dict = dict;
|
|
loop$desired_keys = rest;
|
|
loop$acc = acc;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function do_take(desired_keys, dict) {
|
|
return do_take_loop(dict, desired_keys, to_transient(new$()));
|
|
}
|
|
|
|
/**
|
|
* Creates a new dict from a given dict, only including any entries for which the
|
|
* keys are in a given list.
|
|
*
|
|
* ## Examples
|
|
*
|
|
* ```gleam
|
|
* assert from_list([#("a", 0), #("b", 1)])
|
|
* |> take(["b"])
|
|
* == from_list([#("b", 1)])
|
|
* ```
|
|
*
|
|
* ```gleam
|
|
* assert from_list([#("a", 0), #("b", 1)])
|
|
* |> take(["a", "b", "c"])
|
|
* == from_list([#("a", 0), #("b", 1)])
|
|
* ```
|
|
*/
|
|
export function take(dict, desired_keys) {
|
|
return do_take(desired_keys, dict);
|
|
}
|
|
|
|
function do_combine(combine, left, right) {
|
|
let _block;
|
|
let $1 = size(left) >= size(right);
|
|
if ($1) {
|
|
_block = [left, right, combine];
|
|
} else {
|
|
_block = [right, left, (k, l, r) => { return combine(k, r, l); }];
|
|
}
|
|
let $ = _block;
|
|
let big = $[0];
|
|
let small = $[1];
|
|
let combine$1 = $[2];
|
|
let _pipe = to_transient(big);
|
|
let _pipe$1 = fold(
|
|
small,
|
|
_pipe,
|
|
(transient, key, value) => {
|
|
let update = (existing) => { return combine$1(key, existing, value); };
|
|
return transient_update_with(key, update, value, transient);
|
|
},
|
|
);
|
|
return from_transient(_pipe$1);
|
|
}
|
|
|
|
/**
|
|
* Creates a new dict from a pair of given dicts by combining their entries.
|
|
*
|
|
* If there are entries with the same keys in both dicts the given function is
|
|
* used to determine the new value to use in the resulting dict.
|
|
*
|
|
* ## Examples
|
|
*
|
|
* ```gleam
|
|
* let a = from_list([#("a", 0), #("b", 1)])
|
|
* let b = from_list([#("a", 2), #("c", 3)])
|
|
* assert combine(a, b, fn(one, other) { one + other })
|
|
* == from_list([#("a", 2), #("b", 1), #("c", 3)])
|
|
* ```
|
|
*/
|
|
export function combine(dict, other, fun) {
|
|
return do_combine((_, l, r) => { return fun(l, r); }, dict, other);
|
|
}
|
|
|
|
/**
|
|
* Creates a new dict from a pair of given dicts by combining their entries.
|
|
*
|
|
* If there are entries with the same keys in both dicts the entry from the
|
|
* second dict takes precedence.
|
|
*
|
|
* ## Examples
|
|
*
|
|
* ```gleam
|
|
* let a = from_list([#("a", 0), #("b", 1)])
|
|
* let b = from_list([#("b", 2), #("c", 3)])
|
|
* assert merge(a, b) == from_list([#("a", 0), #("b", 2), #("c", 3)])
|
|
* ```
|
|
*/
|
|
export function merge(dict, new_entries) {
|
|
return combine(dict, new_entries, (_, new_entry) => { return new_entry; });
|
|
}
|
|
|
|
/**
|
|
* Creates a new dict from a given dict with all the same entries except for the
|
|
* one with a given key, if it exists.
|
|
*
|
|
* ## Examples
|
|
*
|
|
* ```gleam
|
|
* assert from_list([#("a", 0), #("b", 1)]) |> delete("a")
|
|
* == from_list([#("b", 1)])
|
|
* ```
|
|
*
|
|
* ```gleam
|
|
* assert from_list([#("a", 0), #("b", 1)]) |> delete("c")
|
|
* == from_list([#("a", 0), #("b", 1)])
|
|
* ```
|
|
*/
|
|
export function delete$(dict, key) {
|
|
let _pipe = to_transient(dict);
|
|
let _pipe$1 = ((_capture) => { return transient_delete(key, _capture); })(
|
|
_pipe,
|
|
);
|
|
return from_transient(_pipe$1);
|
|
}
|
|
|
|
function drop_loop(loop$transient, loop$disallowed_keys) {
|
|
while (true) {
|
|
let transient = loop$transient;
|
|
let disallowed_keys = loop$disallowed_keys;
|
|
if (disallowed_keys instanceof $Empty) {
|
|
return from_transient(transient);
|
|
} else {
|
|
let key = disallowed_keys.head;
|
|
let rest = disallowed_keys.tail;
|
|
loop$transient = transient_delete(key, transient);
|
|
loop$disallowed_keys = rest;
|
|
}
|
|
}
|
|
}
|
|
|
|
function do_drop(disallowed_keys, dict) {
|
|
return drop_loop(to_transient(dict), disallowed_keys);
|
|
}
|
|
|
|
/**
|
|
* Creates a new dict from a given dict with all the same entries except any with
|
|
* keys found in a given list.
|
|
*
|
|
* ## Examples
|
|
*
|
|
* ```gleam
|
|
* assert from_list([#("a", 0), #("b", 1)]) |> drop(["a"])
|
|
* == from_list([#("b", 1)])
|
|
* ```
|
|
*
|
|
* ```gleam
|
|
* assert from_list([#("a", 0), #("b", 1)]) |> drop(["c"])
|
|
* == from_list([#("a", 0), #("b", 1)])
|
|
* ```
|
|
*
|
|
* ```gleam
|
|
* assert from_list([#("a", 0), #("b", 1)]) |> drop(["a", "b", "c"])
|
|
* == from_list([])
|
|
* ```
|
|
*/
|
|
export function drop(dict, disallowed_keys) {
|
|
return do_drop(disallowed_keys, dict);
|
|
}
|
|
|
|
/**
|
|
* Creates a new dict with one entry inserted or updated using a given function.
|
|
*
|
|
* If there was not an entry in the dict for the given key then the function
|
|
* gets `None` as its argument, otherwise it gets `Some(value)`.
|
|
*
|
|
* ## Examples
|
|
*
|
|
* ```gleam
|
|
* let dict = from_list([#("a", 0)])
|
|
* let increment = fn(x) {
|
|
* case x {
|
|
* Some(i) -> i + 1
|
|
* None -> 0
|
|
* }
|
|
* }
|
|
*
|
|
* assert upsert(dict, "a", increment) == from_list([#("a", 1)])
|
|
* ```
|
|
*
|
|
* ```gleam
|
|
* assert upsert(dict, "b", increment) == from_list([#("a", 0), #("b", 0)])
|
|
* ```
|
|
*/
|
|
export function upsert(dict, key, fun) {
|
|
let $ = get(dict, key);
|
|
if ($ instanceof Ok) {
|
|
let value = $[0];
|
|
return insert(dict, key, fun(new $option.Some(value)));
|
|
} else {
|
|
return insert(dict, key, fun(new $option.None()));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calls a function for each key and value in a dict, discarding the return
|
|
* value.
|
|
*
|
|
* Useful for producing a side effect for every item of a dict.
|
|
*
|
|
* ```gleam
|
|
* import gleam/io
|
|
*
|
|
* let dict = from_list([#("a", "apple"), #("b", "banana"), #("c", "cherry")])
|
|
*
|
|
* assert
|
|
* each(dict, fn(k, v) {
|
|
* io.println(k <> " => " <> v)
|
|
* })
|
|
* == Nil
|
|
* // a => apple
|
|
* // b => banana
|
|
* // c => cherry
|
|
* ```
|
|
*
|
|
* The order of elements in the iteration is an implementation detail that
|
|
* should not be relied upon.
|
|
*/
|
|
export function each(dict, fun) {
|
|
return fold(
|
|
dict,
|
|
undefined,
|
|
(nil, k, v) => {
|
|
fun(k, v);
|
|
return nil;
|
|
},
|
|
);
|
|
}
|
|
|
|
function group_loop(loop$transient, loop$to_key, loop$list) {
|
|
while (true) {
|
|
let transient = loop$transient;
|
|
let to_key = loop$to_key;
|
|
let list = loop$list;
|
|
if (list instanceof $Empty) {
|
|
return from_transient(transient);
|
|
} else {
|
|
let value = list.head;
|
|
let rest = list.tail;
|
|
let key = to_key(value);
|
|
let update = (existing) => { return listPrepend(value, existing); };
|
|
let _pipe = transient;
|
|
let _pipe$1 = ((_capture) => {
|
|
return transient_update_with(key, update, toList([value]), _capture);
|
|
})(_pipe);
|
|
loop$transient = _pipe$1;
|
|
loop$to_key = to_key;
|
|
loop$list = rest;
|
|
}
|
|
}
|
|
}
|
|
|
|
export function group(key, list) {
|
|
return group_loop(to_transient(new$()), key, list);
|
|
}
|