Javascript 如何使用Ramda.js更好地实现这一点

Javascript 如何使用Ramda.js更好地实现这一点,javascript,functional-programming,ramda.js,Javascript,Functional Programming,Ramda.js,所以我有一个div列表:list 我想要列表的一个子集,用.fade类删除div。 还可以使用.selected类从div中获取列表 所以使用R.takeWhile和R.dropWhile 然后我想映射这个新列表,并在该列表的子集上添加一个.active类,使用R.take和R.forEach或R.map 比如: var takeFromSelected = R.dropWhile(function(item){!$(item).hasClass('selected')}; var remov

所以我有一个div列表:
list

我想要列表的一个子集,用
.fade
类删除div。 还可以使用
.selected
类从div中获取列表

所以使用
R.takeWhile
R.dropWhile

然后我想映射这个新列表,并在该列表的子集上添加一个
.active
类,使用
R.take
R.forEach
R.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)

我想你问这个问题是因为你得到了一个意想不到的结果

一般来说,addActiveClass函数引入了一个副作用——它修改了DOM。这没有错,但从FP的角度来看,你最好在IO单子中隔离这种效应

函数takeFromSelected和removeFadeItems使用谓词函数(函数(项){!$(项)…};);决定要放下和拿走什么。谓词函数需要返回true或false。在你的情况下,他们不会退回任何东西。解决方法是在
前面添加
return
$(项目)

具有副作用的函数addActiveClass没有返回值。这会中断
管道
,因为下一个函数
removeFadeItems
不会显示任何内容。只需使
addActiveClass
返回$(项目)

我还没有测试它,但是如果您添加三个
return
s,它可能会工作

祝你好运


更新:因为
addActiveClass
返回一个jQuery对象并将其传递给
removeFadeItems
,所以您不需要再次将项目包装在$()中。

根据您的描述,听起来您想使用多于或

takeWhile
保留数组的值,直到谓词第一次失败:

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

> R.takeWhile(R.isEmpty, [[], [], [1, 2, 3], [], [1, 3]])
[ [], [] ]
> 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');
});
此外,正如@donut所说,您还需要返回一个值。然而,你和我的关系有点不好。因为它会改变值(这是一种副作用),所以使用
map
有点用词不当。你最好使用,因为它是为副作用的东西:

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($),
  ...
现在,允许您对对象调用函数。我们想在参数为
active
的jquery包装对象上调用
addClass
。让我们为其创建一个函数:

var addActive = R.invoker(1, 'addClass', 'active');
我们可以将其添加到管道中

var processList = R.pipe(
  R.map($),
  R.forEach(addActive),
  ...
var processList = R.pipe(
  R.map($),
  R.forEach(addActive),
  R.filter(notFaded),
  R.filter(selecteded)
);
过滤器与我们在
addActive
中所做的类似,让我们先通过分离谓词来重构这些过滤器:

var faded = R.invoker(1, 'hasClass', 'fade');
var notFaded = R.not(faded);
var selecteded = R.invoker(1, 'hasClass', 'selected');
这里最棒的事情是ramda函数的可组合性,它允许我们说
R.not(flated)
,而且一切都可以在不考虑它的情况下工作

因此,让我们将其添加到管道中

var processList = R.pipe(
  R.map($),
  R.forEach(addActive),
  ...
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
组合成一个。我们可以通过使用以下参数创建自己的非变异
addClass
来解决此问题:

因此,我们可以再次使用map!。管道可以更改为:

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))
);
我们还可以进行进一步的重构和优化。我们可以在映射之前进行过滤,这样我们就可以迭代更少的元素,或者将
调用程序
调用抽象到一个小小的jquery包装器DSL。我们鼓励您继续重构,但这是一个很好的改变。每个函数都有一个非常重要的作用ittle更具可组合性、可测试性和可理解性

整个重构过程如下所示

之前:

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