diff --git a/data/sidebar_manual_v1200.json b/data/sidebar_manual_v1200.json index a68a5336c..781493d78 100644 --- a/data/sidebar_manual_v1200.json +++ b/data/sidebar_manual_v1200.json @@ -14,6 +14,7 @@ "tuple", "record", "object", + "dict", "variant", "polymorphic-variant", "null-undefined-option", diff --git a/misc_docs/syntax/language_dict.mdx b/misc_docs/syntax/language_dict.mdx new file mode 100644 index 000000000..73d396258 --- /dev/null +++ b/misc_docs/syntax/language_dict.mdx @@ -0,0 +1,51 @@ +--- +id: "dict" +keywords: ["dict"] +name: "dict" +summary: "This is the `dict{}` syntax" +category: "languageconstructs" +--- + +> Available in v12+ + +The `dict{}` syntax is used to represent [dictionaries](dict.md). It's used both when creating dicts, and when pattern matching on dicts. + +### Example + + + +```res +// Create a dict +let d = dict{"A": 5, "B": 6, "C": 7} + +// Pattern match on the full dict +let b = switch d { +| dict{"B": b} => Some(b) +| _ => None +} + +// Destructure the dict +let dict{"C": ?c} = d +``` + +```js +let d = { + A: 5, + B: 6, + C: 7 +}; + +let b = d.B; + +let b$1 = b !== undefined ? b : undefined; + +let c = d.C; +``` + + + +### References + +* [Dictionaries](/docs/manual/latest/dict) + + diff --git a/pages/docs/manual/v12.0.0/dict.mdx b/pages/docs/manual/v12.0.0/dict.mdx new file mode 100644 index 000000000..00b3f2b41 --- /dev/null +++ b/pages/docs/manual/v12.0.0/dict.mdx @@ -0,0 +1,161 @@ +--- +title: "Dictionary" +description: "Dictionary data structure in ReScript" +canonical: "/docs/manual/v12.0.0/dict" +--- + +# Dictionary + +ReScript has first class support for dictionaries. Dictionaries are mutable objects with string keys, where all values must have the same type. Dicts compile to regular JavaScript objects at runtime. + +## Create + +You can create a new dictionary in a few different ways, depending on your use case. + + + +```res prelude +// Using the first class dict syntax +let d = dict{"A": 5, "B": 6} + +// Programatically via the standard library +let d2 = Dict.fromArray([("A", 5), ("B", 6)]) +``` + +```js +let d = { + A: 5, + B: 6 +}; + +let d2 = Object.fromEntries([ + [ + "A", + 5 + ], + [ + "B", + 6 + ] +]); + +``` + + + +A few things to note here: + +* Using the first class `dict{}` syntax compiles cleanly to a JavaScript object directly +* Using `Dict.fromArray` is useful when you need to create a dictionary programatically + +## Access + +You can access values from a Dictionary either via the the standard library `Dict` module functions, or using pattern matching. + + + +```res prelude +let d = dict{"A": 5, "B": 6, "C": 7} + +// Using `Dict.get` +let a = d->Dict.get("A") + +// Switching on the full dict +let b = switch d { +| dict{"B": b} => Some(b) +| _ => None +} + +// Destructuring +let dict{"C": ?c} = d +``` + +```js +let d = { + A: 5, + B: 6, + C: 7 +}; + +let a = d["A"]; + +let b = d.B; + +let b$1 = b !== undefined ? b : undefined; + +let c = d.C; +``` + + +> In the Destructuring example, we're using the `?` optional pattern match syntax to pull out the `C` key value as an optional, regardless of if the dict has it or not. + +## Pattern matching +Dictionaries have first class support for pattern matching. Read more in the [dedicated guide on pattern matching and destructring in ReScript](pattern-matching-destructuring.md#match-on-dictionaries). + +## Updating and setting values + +You can set and update new values on your dictionary using the `Dict.set` function. All updates are mutable. + + + +```res prelude +let d = dict{"A": 5, "B": 6} + +d->Dict.set("C", 7) +``` + +```js +let d = { + A: 5, + B: 6 +}; + +d["C"] = 7; +``` + + + +## Advanced example: Pattern matching on JSON + +JSON objects are represented as dictionaries (`dict`). You can leverage that fact to decode JSON in a nice way, using only language features: + + + +```res prelude +type user = { + name: string, + email: string, +} + +/** Decode JSON to a `user`. +let decodeUser = (json: JSON.t) => { + switch json { + | Object(dict{"name": JSON.String(name), "email": JSON.String(email)}) => + Some({name, email}) + | _ => None + } +} + +``` + +```js +function decodeUser(json) { + if (typeof json !== "object" || json === null || Array.isArray(json)) { + return; + } + let name = json.name; + if (typeof name !== "string") { + return; + } + let email = json.email; + if (typeof email === "string") { + return { + name: name, + email: email + }; + } + +} +``` + + diff --git a/pages/docs/manual/v12.0.0/pattern-matching-destructuring.mdx b/pages/docs/manual/v12.0.0/pattern-matching-destructuring.mdx index b6b0ed587..3913e767d 100644 --- a/pages/docs/manual/v12.0.0/pattern-matching-destructuring.mdx +++ b/pages/docs/manual/v12.0.0/pattern-matching-destructuring.mdx @@ -602,6 +602,63 @@ printStudents({ +### Match on Dictionaries + +You can pattern match on dictionaries just like you can on other ReScript data structures. + +When pattern matching on a dictionary it's assumed by default that you're expecting the keys you match on to exist in the dictionary. Example: + + +```res prelude +let d = dict{"A": 5, "B": 6} + +// We're expecting the `B` key to exist below, and `b` will be `int` in the match branch +let b = switch d { +| dict{"B": b} => Some(b) +| _ => None +} +``` + +```js +let d = { + A: 5, + B: 6 +}; + +let b = d.B; + +let b$1 = b !== undefined ? b : undefined; +``` + + + +However, there are situations where you want to pull out the value of a key as an option. You can do that using the `?` optional syntax in the pattern match: + + + +```res prelude +let d = dict{"A": 5, "B": 6} + +// We're pulling out `B` regardless of if it has a value or not, and therefore get `b` as `option` +let b = switch d { +| dict{"B": ?b} => b +} +``` + +```js +let d = { + A: 5, + B: 6 +}; + +let b = d.B; +``` + + + +Notice how in the first case, when not using `?`, we had to supply a catch-all case `_`. That's because the pattern match _expects_ `B` to exist in the first case, for the pattern to match. If `B` doesn't exist, the match falls through to the next branch, and therefore we need to catch it to be exhaustive in our matching. + +However, in the second case, we don't need a catch-all case. That's because the first branch will _always_ match the dictionary - either `B` exists or it doesn't, but it doesn't matter because we're pulling it out as an optional value. ### Small Pitfall