简体   繁体   English

我怎么能用Ramda.js更好地做到这一点

[英]How can i do this better with Ramda.js

So I have a list of divs: list 所以我有一个div listlist

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.takeWhileR.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.takeR.forEachR.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! 谢谢! :) :)

Update 更新

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而不是takeWhiledropWhile

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);

Refactor 重构

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: 过滤器类似于我们在addActiveaddActive ,让我们首先通过使谓词分离来重构它们:

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没有改变它的参数,我们也可以使用参数化将mapforEach合二为一。 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM