问题描述
我是ReasonML的新手,但我阅读了大多数官方文档。我可以为此进行偶然的尝试和错误,但是由于我现在需要用ReasonML编写代码,因此我想了解迭代 reason record 类型的键和值的最佳实践。
解决方法
也许我不理解问题或用例。但是据我所知,没有办法遍历记录的键/值对。您可能要使用其他数据模型:
- 哈希表https://caml.inria.fr/pub/docs/manual-ocaml/libref/Hashtbl.html
- Js.Dict(如果您使用的是buckscript / ReScript)https://rescript-lang.org/docs/manual/latest/api/js/dict
- 元组列表
有了记录,所有键和值类型都是已知的,因此您只需编写代码即可处理每个键和值,而无需迭代。
,我完全同意@Shawn的观点,即您应该使用更合适的数据结构。例如,元组列表是一种传递用户定义的同类键/值对的好方法:
fooOnThis([
("test1",["a","b","c"]),("test2",["c"]),])
如果您需要异构数据,我建议使用变体来指定数据类型:
type data =
| String(string)
| KvPairs(list((string,data)));
fooOnThis([
("test1",[String("a"),String("b"),String("c")]),[String("c"),KvPairs([("innerTest","d")])]),])
或者,您可以使用对象而不是记录,这似乎是您真正想要的。
对于记录,record需要预定义的记录类型:
type record = {
foo: int,bar: string,};
这是构造它们的方式:
let value = {
foo: 42,bar: "baz",};
另一方面,Objects是结构化类型的,这意味着它们不需要预定义的类型,并且您对它们的构造略有不同:
let value
: {. "foo": int,"bar": string }
= {"foo": 42,"bar": "baz"};
请注意,键是字符串。
对于对象,您可以使用Js.Obj.keys
来获取密钥:
let keys = Js.Obj.keys(value); // returns [|"foo","bar"|]
现在的问题是获取值。没有Js.Obj
API可以获取值或条目,因为这可能不健全或不切实际。为了证明这一点,让我们尝试自己制作。
我们可以轻松地将自己的绑定写到Object.entries
:
[@bs.val] external entries: Js.t({..}) => array((string,_)) = "Object.entries";
entries
是一个函数,该函数可以接收任何对象,并返回一个包含string
键和根据我们如何使用它们推断出的类型的值的元组数组。这既不安全,因为我们不知道实际的值类型是什么,或者因为会被统一键入而特别实用。例如:
let fields = entries({"foo": 42,"bar": "baz"});
// This will infer the value's type as an `int`
switch (fields) {
| [|("foo",value),_|] => value + 2
| _ => 0
};
// This will infer the value's type as an `string`,and yield a type error
// because `fields` can't be typed to hold both `int`s and `string`s
switch (fields) {
| [|("foo",_|] => value ++ "2"
| _ => ""
};
您可以使用这两个switch
表达式中的任何一个(具有意外结果并在运行时崩溃),但不能同时使用这两个表达式,因为在原因中没有要推断的未装箱的string | int
类型。
要解决此问题,我们可以使值成为抽象类型,并使用Js.Types.classify
安全地获取实际的基础数据类型,类似于在JavaScript中使用typeof
:
type value;
[@bs.val] external entries: Js.t({..}) => array((string,value)) = "Object.entries";
let fields = entries({"foo": 42,"bar": "baz"});
switch (fields) {
| [|("foo",_|] =>
switch (Js.Types.classify(value)) {
| JSString(str) => str
| JSNumber(number) => Js.Float.toString(number)
| _ => "unknown"
}
| _ => "unknown"
};
这是完全安全的,但是,正如您所看到的那样,它并不实用。
最后,我们可以进行一些实际修改,以依靠记录在内部以JavaScript对象表示这一事实来安全地将其与记录一起使用。我们所需要做的就是不将entries
限于对象:
[@bs.val] external entries: 'a => array((string,value)) = "Object.entries";
let fields = keys({foo: 42,bar: 24}); // returns [|("foo",42),("bar",24)|]
这仍然是安全的,因为所有值都是JavaScript中的对象,并且我们不对值的类型做任何假设。如果我们尝试将其与原始类型一起使用,我们将得到一个空数组,如果我们尝试将其与数组一起使用,我们将获得索引作为键。
但是因为记录需要预先定义,所以它不会很有用。因此,我仍然建议继续使用元组列表。
注意:这使用的是ReasonML语法,因为这就是您所要的内容,但是指的是ReScript文档,该文档使用略有不同的ReScript语法,因为BuckleScript文档已被删除(是的,现在很混乱,我知道,希望最终会有所改善。)