我来自命令式编程背景(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);
我可以通过更实用的方式完成此任务:(使用JS和Ramda库):
team = R.slice(0, maxTeamSize, R.filter(p => p.skillLevel === 'HIGH', allPlayers));
reserves = R.slice(0, maxReservesSize, R.filter(p => (p.skillLevel === 'HIGH' || p.skillLevel === 'MEDIUM') && !R.contains(p, team), allPlayers));
excluded = R.filter(p => !R.contains(p, team) && !R.contains(p, reserves), allPlayers);
notifyOfInclusion(team.concat(reserves));
notifyOfExclusion(excluded);
但它似乎非常粗糙,重复且不具有说服力。什么是更好(更优雅/声明)的方式从功能POV实现这样的事情?在任何答案中使用Ramda都是奖励,但不是必需的。感谢。
答案 0 :(得分:3)
我的版本比你的版本更具说明性,但只有一点:
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)
这个假设您只想要团队中技能娴熟的球员,即使他们没有足够的球员来填补这些位置。如果你想在这种情况下包括具有MEDIUM技能的玩家,那么你可以用filter(propEq('skillLevel', 'HIGH')
替换reject(propEq('skillLevel', 'LOW')
。如果您想填写max[Team/Reserves]Size
,即使它们与技能级别不匹配,您也可以删除filter
/ reject
来电。 (这也会使代码看起来更清晰。)
我最初尝试使用单个函数对所有这些函数来说非常可怕,它甚至不像这些那样工作:
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
中执行此操作,这可能仍然是您最好的选择,但我怀疑规则与长度之间的相互作用。各种结果列表将允许这里非常漂亮的代码。
所有这些都可以在 Ramda REPL 。
上找到答案 1 :(得分:1)
虽然这个答案并没有真正起作用,但我会将一些逻辑转移到某个变量上,如果有必要,可以重新表示som依赖关系,以便使用统一访问和决策机制的更多组,实际项目必须在该组中进行。< / p>
毕竟逻辑很简单,只需将skillLevel
作为起始级别并迭代到下一个较低级别,直到找到一个长度小于该组给定最大值的组。然后将项目推送到该组。
const
excluded = [], // LOW skilled
reserves = [], // only MEDIUM/HIGH skilled
team = [], // only HIGH skilled
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' }],
maxTeamSize = 2,
maxReservesSize = 2,
temp = { HIGH: team, MEDIUM: reserves, LOW: excluded },
lowerLevel = { HIGH: 'MEDIUM', MEDIUM: 'LOW' },
max = { HIGH: maxTeamSize, MEDIUM: maxReservesSize, LOW: Infinity };
allPlayers.forEach(p => {
var level = p.skillLevel;
while (temp[level].length >= max[level]) {
level = lowerLevel[level];
}
temp[level].push(p);
});
console.log(team);
console.log(reserves);
console.log(excluded);
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
答案 2 :(得分:1)
您正在寻找R.groupBy
:
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');
console.log(R.groupBy(skillLevel, allPlayers));
&#13;
<script src="//cdn.jsdelivr.net/npm/ramda@0.25/dist/ramda.min.js"></script>
&#13;