函数式编程:列表条件分支/过滤(Javascript)

函数式编程:列表条件分支/过滤(Javascript),javascript,functional-programming,ramda.js,Javascript,Functional Programming,Ramda.js,我来自命令式编程背景(Java),开始尝试更好地理解FP概念。特别是条件分支/过滤,以及它如何应用于数据流/数据列表 这里有一个愚蠢的人为的例子…我们有一个球员名单,并希望根据他们的技能水平将他们分为不同的名单。基本的命令式方法可以是: const excluded = []; // LOW skilled const reserves = []; // only MEDIUM/HIGH skilled const team = []; // only HIGH skilled const a

我来自命令式编程背景(Java),开始尝试更好地理解FP概念。特别是条件分支/过滤,以及它如何应用于数据流/数据列表

这里有一个愚蠢的人为的例子…我们有一个球员名单,并希望根据他们的技能水平将他们分为不同的名单。基本的命令式方法可以是:

const excluded = []; // LOW skilled
const reserves = []; // only MEDIUM/HIGH skilled
const team = []; // only HIGH skilled

const allPlayers = [
    {
        name: 'personh1',
        skillLevel: 'HIGH'
    },
    {
        name: 'personh2',
        skillLevel: 'HIGH'
    },
    {
        name: 'personh3',
        skillLevel: 'HIGH'
    },
    {
        name: 'personm1',
        skillLevel: 'MEDIUM'
    },
    {
        name: 'personm2',
        skillLevel: 'MEDIUM'
    },
    {
        name: 'personl1',
        skillLevel: 'LOW'
    },
    {
        name: 'personl2',
        skillLevel: 'LOW'
    }
];

const maxTeamSize = 2;
const maxReservesSize = 2;

allPlayers.forEach(p => {
    if (p.skillLevel === 'HIGH') {
        if (team.length < maxTeamSize) {
            team.push(p);
        } else {
            reserves.push(p);
        }
    } else if (p.skillLevel === 'MEDIUM') {
        if (reserves.length < maxReservesSize) {
            reserves.push(p);
        } else {
            excluded.push(p);
        }
    } else {
        excluded.push(p);
    }
});

// functions defined elsewhere...
notifyOfInclusion(team.concat(reserves));
notifyOfExclusion(excluded);

但它似乎非常粗糙、重复,而且不是很陈述性。从功能性的POV中实现类似的功能,有什么更好的(更优雅的/声明性的)方法?在任何答案中使用Ramda都是一种奖励,但不是必要的。谢谢。

虽然这个答案没有真正的功能性,但我会将一些逻辑移到某个变量,该变量重新表示som依赖关系,以便在必要时使用更多具有统一访问和决策机制的组,实际项目必须进入该组

逻辑毕竟很简单,只需将
skillLevel
作为开始级别,然后迭代到下一个较低的级别,直到找到一个组,该组的长度小于给定的最大值。然后将项目推送到该组

const
排除=[],//低技能
储备=[],//仅中等/高技能
团队=[],//仅限高技能人员
所有玩家=[{name:'personh1',skillLevel:'HIGH'},{name:'personh2',skillLevel:'HIGH'},{name:'personh3',skillLevel:'HIGH'},{name:'person1',skillLevel:'LOW'},{name:'person1',skillLevel LOW'},{name:'person2',skillLevel LOW'},
maxTeamSize=2,
MaxReserveSize=2,
temp={高:团队,中:储备,低:排除},
lowerLevel={HIGH:'MEDIUM',MEDIUM:'LOW'},
max={HIGH:maxTeamSize,MEDIUM:maxReservesSize,LOW:Infinity};
allPlayers.forEach(p=>{
风险等级=p.skillLevel;
while(临时[级别]。长度>=最大[级别]){
级别=较低级别[级别];
}
温度[水平]。推力(p);
});
控制台日志(团队);
控制台日志(保留);
控制台日志(不包括)
.as控制台包装{max height:100%!important;top:0;}
您正在寻找:

const allPlayers=[
{name:'personh1',skillLevel:'HIGH'},
{name:'personh2',skillLevel:'HIGH'},
{name:'personh3',skillLevel:'HIGH'},
{name:'personm1',skillLevel:'MEDIUM'},
{name:'personm2',skillLevel:'MEDIUM'},
{name:'personl1',skillLevel:'LOW'},
{name:'personl2',skillLevel:'LOW'}
];
const skillLevel=R.prop('skillLevel');
log(R.groupBy(skillLevel,allPlayers))

我的版本比你的版本更具声明性,但只有一点:

const skills = groupBy(prop('skillLevel'), allPlayers)
const ordered = flatten([skills['HIGH'], skills['MEDIUM'], skills['LOW']])
const team = filter(propEq('skillLevel', 'HIGH'), take(maxTeamSize, ordered))
const reserves = reject(propEq('skillLevel', 'LOW'), 
                        take(maxReservesSize, drop(length(team), ordered)))
const excluded = drop(length(team) + length(reserves), ordered)
这一条假设你只需要高技能的球员,即使他们没有足够的人来填补空缺。如果您希望在这种情况下包括中等技能的玩家,那么您可以将
过滤器(propEq('skillLevel','HIGH')
替换为
拒绝(propEq('skillLevel','LOW')
。如果您希望填充到
最大[团队/预备队]大小
即使它们与技能级别不匹配,您也可以删除
过滤器
/
拒绝调用(这也会使代码看起来更干净)

我最初尝试为所有这些函数创建一个函数,结果非常糟糕,甚至不像这些函数那样工作:

chain(
  selected => allPlayers => assoc(
    'excluded', 
    difference(allPlayers, concat(selected.team, selected.reserves)), 
    selected
  ),
  pipe(
    groupBy(prop('skillLevel')),
    lift(concat)(prop('HIGH'), prop('MEDIUM')),
    eligible => ({
      team: take(maxTeamSize, eligible),
      reserves: take(maxReservesSize, drop(maxTeamSize, eligible))
    }),
  )
)(allPlayers)
当然,您也可以在排序列表上的单个
reduce
中执行此操作,这可能仍然是解决实际问题的最佳选择,但我怀疑规则和各种结果列表的长度之间的交互是否也会允许在这里生成真正漂亮的代码


所有这些都可以在

上找到,谢谢。我特意简化了逻辑,以表示我感兴趣的更一般的问题。我想我真正想要的是更好地理解如何处理涉及过滤条件相互依赖的不同数据集的问题数据集本身和一些外部状态的变化……然后对每个结果应用一些操作。我很好奇其他人如何更好地以功能/声明的方式构造代码,以及他们将使用什么概念(映射、分组、筛选、组合等)如果你不介意球队中有
MEDIUM
球员,而在后备队中有
LOW
球员,以防没有足够的球员,你可以
R.sortBy
技能水平,然后
R.slice
chain(
  selected => allPlayers => assoc(
    'excluded', 
    difference(allPlayers, concat(selected.team, selected.reserves)), 
    selected
  ),
  pipe(
    groupBy(prop('skillLevel')),
    lift(concat)(prop('HIGH'), prop('MEDIUM')),
    eligible => ({
      team: take(maxTeamSize, eligible),
      reserves: take(maxReservesSize, drop(maxTeamSize, eligible))
    }),
  )
)(allPlayers)