2017-11-18 10 views
3

私は命令型プログラミングのバックグラウンド(Java)であり、FPの概念をよりよく理解しようとしています。特に条件付き分岐/フィルタリング、およびデータのストリーム/リストにどのように適用できるか。機能プログラミング:条件付き分岐/フィルタリングをリストする(JavaScript)

ここでは愚かな人為的な例があります。プレイヤーのリストがあり、スキルレベルに基づいて別のリストに分けたいと考えています。私は、より機能的な方法でこれを達成することができます

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を使用することは、ボーナスになりますが、必須ではありません。ありがとう。

+2

プレイヤーが不足している場合には、チームに「MEDIUM」プレイヤーとリザーブの「LOW」プレイヤーがいらっしゃいましたら、スキルレベルを「R.sortBy」にしてから「R.slice」にしてください。おかげさまで – GingerPlusPlus

答えて

3

マイバージョンあなたより少しより宣言ですが、専用ビット:

この1つは、熟練度の高い選手がチームに欲しいと想定していますその場合に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

この回答は本当に機能的ではありませんが、私はいくつかの変数をいくつかの変数に移動します。必要に応じて、実際の項目をグループ化するための統一されたアクセスと決定メカニズムを持つグループを増やします。

結局のところ論理は単純で、ちょうど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);
.as-console-wrapper { max-height: 100% !important; top: 0; }

+0

私は意図的に、私が興味を持っているより一般的な問題を表すために論理を単純化しました。私は実際には、フィルタリング条件が異なるさまざまなデータセットをフィルタリングするという問題にアプローチする方法を理解しています。データセット自体の変化する状態といくつかの外部状態に依存しています。そして、各結果にいくつかの操作を適用します。私は他の人がどのようにコードを機能的に/宣言的に構造化し、どのような概念を使用するかを知りたいのです(マップ、グループ化、フィルタリング、作成など) –

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));
<script src="//cdn.jsdelivr.net/npm/[email protected]/dist/ramda.min.js"></script>

関連する問題