javascript - 我怎样才能用 Ramda.js 做得更好

标签 javascript functional-programming ramda.js

所以我有一个 div 列表:list我想要列表的子集删除带有 .fade 的 div类(class)。
并且还只是使用 .selected 从 div 中获取列表类(class)。
所以使用 R.takeWhileR.dropWhile .
然后我想映射该新列表并添加 .active在该列表的子集上使用 R.take 进行分类和 R.forEachR.map就像是 :

var takeFromSelected = R.dropWhile(function(item){!$(item).hasClass('selected')};

var removeFadeItems = R.takeWhile(function(item){!$(item).hasClass('fade')});

var addActiveClass = function(x){ $(x).addClass('.active')};

var processList = R.pipe(R.map(addActiveClass), removeFadeItems, takeFromSelected);

processList(list);
我对这个 FP 的东西真的很陌生,并试图掌握它。
任何见解将不胜感激!谢谢! :)
更新
供将来引用,这就是我所做的:
@addActiveClass = (x)->  
  $(x).addClass('active') 
  return
  
@takeFromSelected = R.dropWhile((item)-> !$(item).hasClass('selected'))

@removeFadeItems = R.takeWhile((item)-> !$(item).hasClass('fade'))

@addWeekView = R.compose(addActiveClass, removeFadeItems, takeFromSelected)

最佳答案

根据您的描述,听起来您想使用 filter 超过 takeWhile dropWhile .
takeWhile保持数组的值直到谓词第一次失败:

> R.takeWhile(R.isEmpty, [[], [], [1, 2, 3], [], [1, 3]])
[ [], [] ]
dropWhile删除数组的值,直到谓词第一次失败:
> R.dropWhile(R.isEmpty, [[], [], [1, 2, 3], [], [1, 3]])
[ [ 1, 2, 3 ], [], [ 1, 3 ] ]
filter删除所有未通过谓词的值。
> R.filter(R.isEmpty, [[], [], [1, 2, 3], [], [1, 3]])
[ [], [], [] ]

在你的情况下,你想要类似的东西:
var removeFadeItems = R.filter(function(x) {
  return !$(x).hasClass('fade');
});
var takeFromSelected = R.filter(function(x) {
  return $(x).hasClass('selected');
});

此外,正如@donnut 所说,您的 map 也需要返回一个值。但是,您对 addClass 的态度不太好。 .因为它会改变值(这是一个副作用),所以使用 map有点用词不当。你最好使用 forEach ,因为它是为副作用而设计的:
var addActiveClass = function(x) {
  $(x).addClass('active');
};

所以你最终得到:
var processList = R.pipe(
  R.forEach(addActiveClass),
  takeFromSelected,
  removeFadeItems
);
processList(list);

重构

现在,由于您的某些函数是引用透明的(它们不会改变事物),您可以将其重构为更清晰、更可组合且更高效。

首先要注意的是,您要在每个函数中重新包装 div。 $是一个很好的功能,可以用来包装东西一次。因此,让我们以此开始管道。
var processList = R.pipe(
  R.map($),
  ...

现在, invoker 允许您在对象上调用函数。我们想调用addClass在带有 active 参数的 jquery 包装对象上.让我们为此创建一个函数:
var addActive = R.invoker(1, 'addClass', 'active');

我们可以将其添加到管道中。
var processList = R.pipe(
  R.map($),
  R.forEach(addActive),
  ...

过滤器类似于我们在 addActive 中所做的。 ,让我们首先通过将谓词分开来重构它们:
var faded = R.invoker(1, 'hasClass', 'fade');
var notFaded = R.not(faded);
var selecteded = R.invoker(1, 'hasClass', 'selected');

这里最棒的是 ramda 函数的可组合性让我们可以说 R.not(faded) ,而事情只是在不考虑它的情况下工作。

因此,让我们将其添加到管道中。
var processList = R.pipe(
  R.map($),
  R.forEach(addActive),
  R.filter(notFaded),
  R.filter(selecteded)
);

这似乎并没有改变很多处理的样子。这很好!原语发生了变化,它们更简单,更容易看到正在发生的事情,但整体流程是相同的。

现在是时候开始兴奋了。由于参数化,您可以将两个过滤器组合在一起,而不必担心它们是否有意义。有一条法律规定R.pipe(R.filter(p), R.filter(q)) == R.pipe(R.filter(R.and(p, q)) .这意味着您不必过滤数组两次,只需过滤一次并依次应用谓词。
var processList = R.pipe(
  R.map($),
  R.forEach(addActive),
  R.filter(R.and(notFaded, selecteded))
);

如果 addClass没有改变它的论点,我们也可以使用参数来组合 mapforEach合而为一。我们可以通过制作我们自己的非变异 addClass 来解决这个问题。与 clone :
var newActive = R.pipe(
  R.invoker(0, 'clone'), 
  R.invoker(1, 'addClass', 'active')
);

所以我们可以再次使用 map !管道可以更改为:
var processList = R.pipe(
  R.map($),
  R.map(newActive),
  R.filter(R.and(notFaded, selecteded))
);

我们现在可以使用参数化将 map 组合在一起。法律规定R.pipe(R.map(f), R.map(g)) == R.map(R.pipe(f, g)) .我们不是在数组上映射两次,而是映射一次,然后依次在映射中组合函数。所以我们的管道现在看起来像这样:
var processList = R.pipe(
  R.map(R.pipe($, newActive)),
  R.filter(R.and(notFaded, selecteded))
);

我们可以进行进一步的重构和优化。我们可以在映射之前进行过滤,以便最终迭代更少的元素,或者抽象 invoker调用一个小的 jquery 包装器 DSL。我们鼓励您继续重构,但这与原始版本相比是一个相当不错的变化。每个函数都做的很少,更可组合,更可测试,更容易理解。

整个重构如下。

前:
var removeFadeItems = R.filter(function(x) {
  return !$(x).hasClass('fade');
});
var takeFromSelected = R.filter(function(x) {
  return $(x).hasClass('selected');
});
var addActiveClass = function(x) {
  $(x).addClass('active');
};

var processList = R.pipe(
  R.forEach(addActiveClass),
  takeFromSelected,
  removeFadeItems
);

processList(list);

后:
var faded      = R.invoker(1, 'hasClass', 'fade');
var selecteded = R.invoker(1, 'hasClass', 'selected');
var notFaded = R.not(faded);
var newActive = R.pipe(
  R.invoker(0, 'clone'), 
  R.invoker(1, 'addClass', 'active')
);

var processList = R.pipe(
  R.map(R.pipe($, newActive)),
  R.filter(R.and(notFaded, selecteded))
);

processList(list);

关于javascript - 我怎样才能用 Ramda.js 做得更好,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28896800/

相关文章:

javascript - 如何在 MVC 5 中更新模型

javascript - 将 div 移动到标题顶部

javascript - 使用映射、任意参数在 Javascript 中重建 _zip

javascript - 在 ramda.js 中使用嵌套字段进行排序

javascript - 具有多个谓词的过滤器,优雅的函数式方法

javascript - 迭代父元素并获取文本节点

javascript - JS 缩小 : Compressing the code

functional-programming - OCaml 中的 `string` 是否支持 UTF-8?

clojure - 如何在 Clojure 中实现并行逻辑或提前终止

javascript - Ramda.js 如何用挖掘对象进行采摘