根据您的描述,听起来您想使用filter
more than takeWhile
or 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
. 由于它会改变值(这是一个副作用),因此 usingmap
有点用词不当。你最好使用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
用参数调用 jquery 包装的对象active
。让我们为此创建一个函数:
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
不改变它的论点,我们也可以使用参数化将map
and合二为一forEach
。我们可以通过使我们自己的非变异来解决这个addClass
问题clone
:
var newActive = R.pipe(
R.invoker(0, 'clone'),
R.invoker(1, 'addClass', 'active')
);
所以我们可以再次使用地图!管道可以更改为:
var processList = R.pipe(
R.map($),
R.map(newActive),
R.filter(R.and(notFaded, selecteded))
);
我们现在可以使用参数化将地图组合在一起。法律规定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);