[英]How can i do this better with Ramda.js
So I have a list of divs: list
所以我有一个div
list
: list
i want a subset of the list removing the divs with the .fade
class. 我希望列表的一个子集删除
.fade
类的div。 and also just grabbing the list from the div with .selected
class. 并且只是使用
.selected
类从div中获取列表。
so using R.takeWhile
and R.dropWhile
. 所以使用
R.takeWhile
和R.dropWhile
。
then i want to map over that new list and add a .active
class on a subset of that list with R.take
and R.forEach
or R.map
然后我想映射该新列表并使用
R.take
和R.forEach
或R.map
在该列表的子集上添加.active
类
something like : 就像是 :
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);
Im really new to this FP stuff and trying to get the grip of it. 我对这个FP的东西真的很陌生并试图掌握它。
Any insight would be greatly apreciated!! 任何洞察力都会大大降低! Thanks!
谢谢! :)
:)
for future reference this is what i did : 为了将来参考,这就是我所做的:
@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)
From your description, it sounds like you want to use filter
more than takeWhile
or dropWhile
. 根据您的描述,听起来您想要使用
filter
而不是takeWhile
或dropWhile
。
takeWhile
keeps values of the array until the first time the predicate fails: takeWhile
保留数组的值,直到谓词第一次失败:
> R.takeWhile(R.isEmpty, [[], [], [1, 2, 3], [], [1, 3]])
[ [], [] ]
dropWhile
removes values of the array until the first time the predicate fails: dropWhile
删除数组的值,直到第一次谓词失败:
> R.dropWhile(R.isEmpty, [[], [], [1, 2, 3], [], [1, 3]])
[ [ 1, 2, 3 ], [], [ 1, 3 ] ]
filter
removes all values that do not pass the predicate. filter
将删除所有未传递谓词的值。
> R.filter(R.isEmpty, [[], [], [1, 2, 3], [], [1, 3]])
[ [], [], [] ]
In your case you want something like: 在你的情况下你想要的东西:
var removeFadeItems = R.filter(function(x) {
return !$(x).hasClass('fade');
});
var takeFromSelected = R.filter(function(x) {
return $(x).hasClass('selected');
});
Also, as @donnut stated, your map
needs to return a value as well. 另外,正如@donnut所说,你的
map
需要返回一个值。 However, you're kind of in a bad way with addClass
. 但是,使用
addClass
会让你感觉很糟糕。 Since it mutates the value (which is a side-effect), using map
is a bit of a misnomer. 由于它改变了值(这是副作用),因此使用
map
有点用词不当。 You're better off using forEach
, as it is made for side-effecting things: 你最好使用
forEach
,因为它是用于副作用的东西:
var addActiveClass = function(x) {
$(x).addClass('active');
};
So you end up with: 所以你最终得到:
var processList = R.pipe(
R.forEach(addActiveClass),
takeFromSelected,
removeFadeItems
);
processList(list);
Now, being that some of your functions are referentially transparent (they don't mutate things), you can refactor this to be a bit clearer, more composable and slightly more efficient. 现在,由于你的某些函数是引用透明的(它们不会改变事物),你可以将它重构为更清晰,更可组合和更高效。
The first thing to note is that you're rewrapping your divs in each function. 首先要注意的是你在每个函数中重写你的div。 The
$
is a good function to use to wrap things just once. $
是一个很好的功能,只用一次包装东西。 So let's start the pipeline with that. 那么让我们开始管道。
var processList = R.pipe(
R.map($),
...
Now, invoker
allows you to call a function on an object. 现在,
invoker
允许您调用对象上的函数。 We want to call addClass
on the jquery wrapped object with the argument of active
. 我们想在jquery包装对象上使用
active
参数调用addClass
。 Let's make a function for that: 让我们为此做一个函数:
var addActive = R.invoker(1, 'addClass', 'active');
We can add this to the pipeline. 我们可以将其添加到管道中。
var processList = R.pipe(
R.map($),
R.forEach(addActive),
...
The filters are similar to what we did in addActive
, let's refactor those too first by making the predicates separate: 过滤器类似于我们在
addActive
中addActive
,让我们首先通过使谓词分离来重构它们:
var faded = R.invoker(1, 'hasClass', 'fade');
var notFaded = R.not(faded);
var selecteded = R.invoker(1, 'hasClass', 'selected');
The great thing here is the composability of ramda functions allows us to say R.not(faded)
, and things just work without thinking about it. 这里最棒的是ramda函数的可组合性允许我们说
R.not(faded)
,而事情只是工作而不考虑它。
So lets add this to the pipeline. 所以我们将它添加到管道中。
var processList = R.pipe(
R.map($),
R.forEach(addActive),
R.filter(notFaded),
R.filter(selecteded)
);
This doesn't seem to have changed much of what the processing looks like. 这似乎没有改变处理的大部分内容。 This is good!
这很好! The primitives have changed, they're simpler and easier to see what's going on, but the overall flow is the same.
原语已经改变,它们更简单,更容易看到发生了什么,但整体流程是相同的。
Now it's time to get heady. 现在是时候变得令人兴奋了。 Because of parametricity, you can combine the two filters together without worrying about whether or not they make sense.
由于参数化,您可以将两个滤波器组合在一起,而无需担心它们是否有意义。 There's a law that states
R.pipe(R.filter(p), R.filter(q)) == R.pipe(R.filter(R.and(p, q))
. What this means is that you do not have to filter the array twice, you can filter it just once and apply the predicates in turn. 有一条法则规定
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))
);
If addClass
did not mutate its argument, we could also use parametricity to combine the map
and forEach
into one. 如果
addClass
没有改变它的参数,我们也可以使用参数化将map
和forEach
合二为一。 We can solve this by making our own non-mutating addClass
with clone
: 我们可以通过使用
clone
创建我们自己的非变异addClass
来解决这个问题:
var newActive = R.pipe(
R.invoker(0, 'clone'),
R.invoker(1, 'addClass', 'active')
);
So we can use map again!. 所以我们可以再次使用地图! The pipeline can change to:
管道可以改为:
var processList = R.pipe(
R.map($),
R.map(newActive),
R.filter(R.and(notFaded, selecteded))
);
And we can now use parametricity to combine the maps together. 现在我们可以使用参数化将地图组合在一起。 The law states
R.pipe(R.map(f), R.map(g)) == R.map(R.pipe(f, g))
. 法则规定
R.pipe(R.map(f), R.map(g)) == R.map(R.pipe(f, g))
。 Instead of mapping twice over the array, we map once, and compose the functions within the map in turn. 我们不是在数组上映射两次,而是映射一次,然后依次组合映射中的函数。 So our pipeline now looks like this:
所以我们的管道现在看起来像这样:
var processList = R.pipe(
R.map(R.pipe($, newActive)),
R.filter(R.and(notFaded, selecteded))
);
There are further refactorings and optimizations we can make. 我们可以进行进一步的重构和优化。 We could filter before mapping so we end up iterating over less elements, or abstract the
invoker
calls to a little jquery wrapper DSL. 我们可以在映射之前进行过滤,因此我们最终会迭代更少的元素,或者将
invoker
调用抽象为一个小的jquery包装器DSL。 You're encouraged to continue with the refactoring, but this is a pretty nice change from the original. 我们鼓励你继续进行重构,但这是一个非常好的改变。 Each of functions does a very little, is much more composable, more testable, and more understandable.
每个函数都做得很少,更可组合,更可测试,更易理解。
The whole refactoring is as follows. 整个重构如下。
Before: 之前:
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);
After: 后:
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);
I suppose you ask this question because you get an unexpected result. 我想你问这个问题是因为你得到了意想不到的结果。
A general remark is that the function addActiveClass introduces a side-effect — it modifies the DOM. 一般说法是函数addActiveClass引入了副作用 - 它修改了DOM。 This isn't wrong, but from a FP point of view you better isolate this effect, eq in a IO monad.
这没有错,但从FP的角度来看,你最好在IO monad中隔离这种效果。
The function takeFromSelected and removeFadeItems use predicate functions (function(item){ !$(item) ... };); 函数takeFromSelected和removeFadeItems使用谓词函数(function(item){!$(item)...};); to determine what to drop and take.
确定要丢弃和采取什么。 A predicate function needs to return true or false.
谓词函数需要返回true或false。 In your case they do not return anything.
在你的情况下,他们不会返回任何东西。 Solution is to add
return
in front of !$(item)
解决方案是在前面添加
return
!$(item)
The function addActiveClass, the one with the side-effect, does not have a return value. addActiveClass函数(具有副作用的函数)没有返回值。 This breaks the
pipeline
as the next function removeFadeItems
will not reveive anything. 这会破坏
pipeline
因为下一个函数removeFadeItems
将不会显示任何内容。 Just make addActiveClass
return the $(item). 只需使
addActiveClass
返回$(item)即可。
I haven't tested it, but if you add the three return
s, it will probably work. 我没有测试它,但如果你添加三个
return
s,它可能会工作。
Good luck. 祝好运。
update: because addActiveClass
returns a jQuery object and passes that on to removeFadeItems
you do not need to wrap item in a $() again. 更新:因为
addActiveClass
返回一个jQuery对象并将其传递给removeFadeItems
您不需要再次将项目包装在$()中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.