【发布时间】:2020-04-27 19:02:50
【问题描述】:
我有一个过滤器函数,它本质上具有多个确定过滤逻辑的变量。如果定义了变量,我想过滤——如果没有,我不想过滤(即在管道中执行函数)。更一般地说,有一个谓词,我可以检查管道的每个参数,以确定我应该调用它还是只传递给下一个函数。
我这样做是为了防止复杂的分支逻辑,但对于函数式编程来说还很陌生,我认为这是重构的最佳方式。
例如:
resources = R.pipe(
filterWithRadius(lat, lng, radius), // if any of these arguments are nil, act like R.identity
filterWithOptions(filterOptions)(keyword), // if either filterOptions or keyword is nil, act like R.identity
filterWithOptions(tagOptions)(tag) // same as above.
)(resources);
我正在考虑使用R.unless/R.when,但它似乎不适用于具有多个参数的函数。如果 R.pipeWith 处理的是函数参数,那么这里会很有用。
作为一个示例实现:
const filterWithRadius = R.curry((lat, long, radius, resources) =>
R.pipe(
filterByDistance(lat, long, radius), // simply filters down a geographic location, will fail if any of lat/long/radius are not defined
R.map(addDistanceToObject(lat, long)), // adds distance to the lat and long to prop distanceFromCenter
R.sortBy(R.prop("distanceFromCenter")) // sorts by distance
)(resources)
);
resources 是这些资源对象的数组。从本质上讲,filterRadius 和 filterOptions 这两个函数都是纯函数,需要一个资源数组和 有效 参数(非未定义)并输出一个新的过滤列表。所以这里的目标是以某种方式组合(或重构),如果参数都是未定义的,它将运行函数,否则只是充当身份。
还有比这更干净/更好的方法吗?
resources = R.pipe(
lat && lng && radius
? filterWithRadius(lat, lng, radius)
: R.identity,
keyword ? filterWithOptions(filterOptions)(keyword) : R.identity,
tag ? filterWithOptions(tagOptions)(tag) : R.identity
)(resources);
【问题讨论】:
-
请添加函数的实现、输入的数据和预期的结果。
-
@OriDrori 我已经添加了我的部分实现以及预期的结果。如果还有什么不清楚的地方请告诉我。
-
如何将资源旁边的所有参数传递给管道?
-
@OriDrori 管道包含在路由处理程序中,我在其中解构
req.query以便lat是req.query.lat如果它存在,例如。因此,根据req.query中的属性,可能会或可能不会定义一些变量。所以我正在尝试重构复杂的分支逻辑(之前我们有多个嵌套的 if 语句来进行过滤)。 -
我认为用不常见的组合子(在 Ramda 之外)对条件分支进行编码不是一个好主意,因为它会混淆代码。如果您需要表达式并相应地拆分管道,只需使用
if/else语句或条件运算符。
标签: functional-programming ramda.js