diff --git a/CHANGELOG.md b/CHANGELOG.md index cd9cd23e47..98822371cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ #### :rocket: New Feature +- Add `Dict.has` and double `Dict.forEachWithKey`/`Dict.mapValues` performance. https://github.com/rescript-lang/rescript/pull/7316 - Add popover attributes to JsxDOM.domProps. https://github.com/rescript-lang/rescript/pull/7317 #### :house: Internal diff --git a/lib/es6/Stdlib_Dict.js b/lib/es6/Stdlib_Dict.js index 4d12162214..9a472ed5c4 100644 --- a/lib/es6/Stdlib_Dict.js +++ b/lib/es6/Stdlib_Dict.js @@ -10,21 +10,25 @@ function forEach(dict, f) { } function forEachWithKey(dict, f) { - Object.entries(dict).forEach(param => f(param[1], param[0])); + Object.keys(dict).forEach(key => f(dict[key], key)); } function mapValues(dict, f) { let target = {}; - forEachWithKey(dict, (value, key) => { + Object.keys(dict).forEach(key => { + let value = dict[key]; target[key] = f(value); }); return target; } +let has = ((dict, key) => key in dict); + export { $$delete$1 as $$delete, forEach, forEachWithKey, mapValues, + has, } /* No side effect */ diff --git a/lib/js/Stdlib_Dict.js b/lib/js/Stdlib_Dict.js index 4e8aab1fe0..28788f4ec1 100644 --- a/lib/js/Stdlib_Dict.js +++ b/lib/js/Stdlib_Dict.js @@ -10,19 +10,23 @@ function forEach(dict, f) { } function forEachWithKey(dict, f) { - Object.entries(dict).forEach(param => f(param[1], param[0])); + Object.keys(dict).forEach(key => f(dict[key], key)); } function mapValues(dict, f) { let target = {}; - forEachWithKey(dict, (value, key) => { + Object.keys(dict).forEach(key => { + let value = dict[key]; target[key] = f(value); }); return target; } +let has = ((dict, key) => key in dict); + exports.$$delete = $$delete$1; exports.forEach = forEach; exports.forEachWithKey = forEachWithKey; exports.mapValues = mapValues; +exports.has = has; /* No side effect */ diff --git a/runtime/Stdlib_Dict.res b/runtime/Stdlib_Dict.res index 0c5b82372f..7393c4fb61 100644 --- a/runtime/Stdlib_Dict.res +++ b/runtime/Stdlib_Dict.res @@ -28,8 +28,9 @@ let forEach = (dict, f) => { dict->valuesToArray->Stdlib_Array.forEach(value => f(value)) } +@inline let forEachWithKey = (dict, f) => { - dict->toArray->Stdlib_Array.forEach(((key, value)) => f(value, key)) + dict->keysToArray->Stdlib_Array.forEach(key => f(dict->getUnsafe(key), key)) } let mapValues = (dict, f) => { @@ -39,3 +40,5 @@ let mapValues = (dict, f) => { }) target } + +let has: (dict<'a>, string) => bool = %raw(`(dict, key) => key in dict`) diff --git a/runtime/Stdlib_Dict.resi b/runtime/Stdlib_Dict.resi index 94b5c14d52..64f3c175dc 100644 --- a/runtime/Stdlib_Dict.resi +++ b/runtime/Stdlib_Dict.resi @@ -17,7 +17,7 @@ Use `Dict.getUnsafe` only when you are sure the key exists (i.e. when iterating ## Examples ```rescript -let dict = Dict.fromArray([("key1", "value1"), ("key2", "value2")]) +let dict = dict{"key1": "value1", "key2": "value2"} let value = dict->Dict.getUnsafe("key1") Console.log(value) // value1 ``` @@ -30,7 +30,7 @@ Returns the value at the provided key, if it exists. Returns an option. ## Examples ```rescript -let dict = Dict.fromArray([("someKey", "someValue")]) +let dict = dict{"someKey": "someValue"} switch dict->Dict.get("someKey") { | None => Console.log("Nope, didn't have the key.") @@ -59,7 +59,7 @@ external set: (dict<'a>, string, 'a) => unit = "" ## Examples ```rescript -let dict = Dict.fromArray([("someKey", "someValue")]) +let dict = dict{"someKey": "someValue"} dict->Dict.delete("someKey") ``` @@ -189,7 +189,7 @@ external assign: (dict<'a>, dict<'a>) => dict<'a> = "Object.assign" ## Examples ```rescript -let dict = Dict.fromArray([("key1", "value1"), ("key2", "value2")]) +let dict = dict{"key1": "value1", "key2": "value2"} let dict2 = dict->Dict.copy // Both log `["key1", "key2"]` here. @@ -206,7 +206,7 @@ external copy: (@as(json`{}`) _, dict<'a>) => dict<'a> = "Object.assign" ## Examples ```rescript -let dict = Dict.fromArray([("key1", "value1"), ("key2", "value2")]) +let dict = dict{"key1": "value1", "key2": "value2"} dict->Dict.forEach(value => { Console.log(value) @@ -220,7 +220,7 @@ let forEach: (dict<'a>, 'a => unit) => unit ## Examples ```rescript -let dict = Dict.fromArray([("key1", "value1"), ("key2", "value2")]) +let dict = dict{"key1": "value1", "key2": "value2"} dict->Dict.forEachWithKey((value, key) => { Console.log2(value, key) @@ -235,10 +235,25 @@ let forEachWithKey: (dict<'a>, ('a, string) => unit) => unit ## Examples ```rescript -let dict = Dict.fromArray([("key1", 1), ("key2", 2)]) +let dict = dict{"key1": 1, "key2": 2} dict->Dict.mapValues(v => v + 10)->Dict.toArray // [("key1", 11), ("key2", 12)] dict->Dict.mapValues(v => Int.toString(v))->Dict.toArray // [("key1", "1"), ("key2", "2")] ``` */ let mapValues: (dict<'a>, 'a => 'b) => dict<'b> + +/** +`has(dictionary, "key")` returns true if the "key" is present in the dictionary. + +## Examples + +```rescript +let dict = dict{"key1": Some(1), "key2": None} + +dict->Dict.has("key1") // true +dict->Dict.has("key2") // true +dict->Dict.has("key3") // false +``` +*/ +let has: (dict<'a>, string) => bool diff --git a/tests/tests/src/DictTests.mjs b/tests/tests/src/DictTests.mjs index a1a763250b..b57778e88f 100644 --- a/tests/tests/src/DictTests.mjs +++ b/tests/tests/src/DictTests.mjs @@ -1,5 +1,6 @@ // Generated by ReScript, PLEASE EDIT WITH CARE +import * as Stdlib_Dict from "rescript/lib/es6/Stdlib_Dict.js"; let someString = "hello"; @@ -41,6 +42,51 @@ let PatternMatching = { constrainedAsDict: constrainedAsDict }; +let dict = { + key1: 1, + key2: undefined +}; + +if (Stdlib_Dict.has(dict, "key1") !== true) { + throw { + RE_EXN_ID: "Assert_failure", + _1: [ + "DictTests.res", + 43, + 2 + ], + Error: new Error() + }; +} + +if (Stdlib_Dict.has(dict, "key2") !== true) { + throw { + RE_EXN_ID: "Assert_failure", + _1: [ + "DictTests.res", + 44, + 2 + ], + Error: new Error() + }; +} + +if (Stdlib_Dict.has(dict, "key3") !== false) { + throw { + RE_EXN_ID: "Assert_failure", + _1: [ + "DictTests.res", + 45, + 2 + ], + Error: new Error() + }; +} + +let DictHas = { + dict: dict +}; + let three = 3; export { @@ -49,5 +95,6 @@ export { three, intDict, PatternMatching, + DictHas, } -/* No side effect */ +/* Not a pure module */ diff --git a/tests/tests/src/DictTests.res b/tests/tests/src/DictTests.res index e1d2523cac..feb62ade9f 100644 --- a/tests/tests/src/DictTests.res +++ b/tests/tests/src/DictTests.res @@ -33,3 +33,14 @@ module PatternMatching = { | _ => Js.log("not one") } } + +module DictHas = { + let dict = dict{ + "key1": Some(1), + "key2": None, + } + + assert(dict->Dict.has("key1") === true) + assert(dict->Dict.has("key2") === true) + assert(dict->Dict.has("key3") === false) +}