JavaScriptで各生徒の上位n件の得点平均を求める方法
問題の概要
次のような、生徒のID(id)と一定期間に記録された得点(score)を保持するオブジェクトの配列があるとします。
const marks = [
{ id: 231, score: 34 },
{ id: 233, score: 37 },
{ id: 231, score: 31 },
{ id: 233, score: 39 },
{ id: 231, score: 44 },
{ id: 233, score: 41 },
{ id: 231, score: 38 },
{ id: 231, score: 31 },
{ id: 233, score: 29 },
{ id: 231, score: 34 },
{ id: 233, score: 40 },
{ id: 231, score: 31 },
{ id: 231, score: 30 },
{ id: 233, score: 38 },
{ id: 231, score: 43 },
{ id: 233, score: 42 },
{ id: 233, score: 28 },
{ id: 231, score: 33 },
];
この配列を第1引数として、さらに数値(num)を第2引数として受け取るJavaScript関数を作成します。
関数は、各生徒(一意のid)ごとにscoreプロパティの値が高い順に上位num件のレコードを取り出し、その平均値を計算します。ある生徒のレコード数がnumに満たない場合は、その生徒のすべてのレコードを平均の対象とします。
そして最後に、生徒のidをキー、平均得点を値にもつオブジェクトを返します。
サンプルコード
const marks = [
{ id: 231, score: 34 },
{ id: 233, score: 37 },
{ id: 231, score: 31 },
{ id: 233, score: 39 },
{ id: 231, score: 44 },
{ id: 233, score: 41 },
{ id: 231, score: 38 },
{ id: 231, score: 31 },
{ id: 233, score: 29 },
{ id: 231, score: 34 },
{ id: 233, score: 40 },
{ id: 231, score: 31 },
{ id: 231, score: 30 },
{ id: 233, score: 38 },
{ id: 231, score: 43 },
{ id: 233, score: 42 },
{ id: 233, score: 28 },
{ id: 231, score: 33 },
];
const calculateHighestAverage = (marks = [], num = 1) => {
// 生徒ごとに得点をグループ化
const grouped = {};
for (const { id, score } of marks) {
if (grouped.hasOwnProperty(id)) {
grouped[id].push(score);
} else {
grouped[id] = [score];
}
}
// 上位num件の平均を計算
const res = {};
for (const id in grouped) {
const topScores = grouped[id]
.sort((a, b) => b - a)
.slice(0, num);
const sum = topScores.reduce((acc, val) => acc + val, 0);
res[id] = sum / topScores.length;
}
return res;
};
console.log(calculateHighestAverage(marks, 5));
console.log(calculateHighestAverage(marks, 4));
console.log(calculateHighestAverage(marks));
コードの解説
処理の流れは次のとおりです。
- グループ化: for...ofループで配列を走査し、分割代入によってidとscoreを取り出します。初めて登場したidであれば新しい配列を作成し、すでに存在するidであればその配列にscoreを追加していきます。
- 上位n件の抽出: 各生徒の得点配列をsortメソッドで降順に並べ替え、slice(0, num)で先頭からnum件を取得します。要素数がnumより少ない場合でもエラーにはならず、存在するすべての要素が対象になります。
- 平均の計算: reduceで合計値を求めたうえで要素数で割ることで、正確な平均値を算出します。reduceの初期値として0を指定しているため、空の配列が渡されても安全に動作します。
実行結果
コンソールには次のように出力されます。
{ '231': 38.6, '233': 40 }
{ '231': 39.75, '233': 40.5 }
{ '231': 44, '233': 42 }
1回目の呼び出しでは上位5件、2回目では上位4件、3回目ではデフォルト値の1件(つまり各生徒の最高得点のみ)の平均がそれぞれ表示されています。このように、引数numを変えるだけで柔軟に集計条件を変更できるのがポイントです。
-
JavaScriptでオブジェクトのメンバー(プロパティ・メソッド)を設定する方法
JavaScriptでは、オブジェクトのメンバー(プロパティやメソッド)をドット記法を使って簡単に設定・追加できます。空のオブジェクトを作成した後でも、後から自由に新しいメンバーを動的に割り当てられるのがJavaScriptの柔軟な特徴です。 ここでは、実際のコード例を通じて、オブジェクトへのメンバー設定の基本を解説します。 コード例 以下は、JavaScriptでオブジェクトのメンバーを設定するサンプルコードです。 <!DOCTYPE html> <html lang=ja> <head> <meta charset=UTF-8 /> <
-
JavaScriptで連結リストの各ノードの「次に大きい値」を効率的に求める方法
問題概要JavaScriptで、連結リストの先頭ノード(head)を唯一の引数として受け取る関数を作成することを考えます。この連結リストには数値データが格納されており、リスト内の各ノードには「次に大きい値(next larger value)」が存在する場合があります。ノードiに対して next_larger(node_i) とは、j > i かつ node_j.val > node_i.val を満たすノードの中で、j が最小になるような node_j.val のことです。そのような j が存在しない場合、次に大きい値は 0 となります。つまり私たちの関数は、リスト内の各要素に対