在Reasonml中迭代记录键和值的最佳实践是什么?

问题描述

我是ReasonML的新手,但我阅读了大多数官方文档。我可以为此进行偶然的尝试和错误,但是由于我现在需要用ReasonML编写代码,因此我想了解迭代 reason record 类型的键和值的最佳实践。

解决方法

也许我不理解问题或用例。但是据我所知,没有办法遍历记录的键/值对。您可能要使用其他数据模型:

有了记录,所有键和值类型都是已知的,因此您只需编写代码即可处理每个键和值,而无需迭代。

,

我完全同意@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文档已被删除(是的,现在很混乱,我知道,希望最终会有所改善。)