[英]How can i do this better with Ramda.js
所以我有一个div list
: list
我希望列表的一个子集删除.fade
类的div。 并且只是使用.selected
类从div中获取列表。
所以使用R.takeWhile
和R.dropWhile
。
然后我想映射该新列表并使用R.take
和R.forEach
或R.map
在该列表的子集上添加.active
类
就像是 :
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
允许您调用对象上的函数。 我们想在jquery包装对象上使用active
参数调用addClass
。 让我们为此做一个函数:
var addActive = R.invoker(1, 'addClass', 'active');
我们可以将其添加到管道中。
var processList = R.pipe(
R.map($),
R.forEach(addActive),
...
过滤器类似于我们在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
和forEach
合二为一。 我们可以通过使用clone
创建我们自己的非变异addClass
来解决这个问题:
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);
我想你问这个问题是因为你得到了意想不到的结果。
一般说法是函数addActiveClass引入了副作用 - 它修改了DOM。 这没有错,但从FP的角度来看,你最好在IO monad中隔离这种效果。
函数takeFromSelected和removeFadeItems使用谓词函数(function(item){!$(item)...};); 确定要丢弃和采取什么。 谓词函数需要返回true或false。 在你的情况下,他们不会返回任何东西。 解决方案是在前面添加return
!$(item)
addActiveClass函数(具有副作用的函数)没有返回值。 这会破坏pipeline
因为下一个函数removeFadeItems
将不会显示任何内容。 只需使addActiveClass
返回$(item)即可。
我没有测试它,但如果你添加三个return
s,它可能会工作。
祝好运。
更新:因为addActiveClass
返回一个jQuery对象并将其传递给removeFadeItems
您不需要再次将项目包装在$()中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.