JavaScript
 Computer >> コンピューター >  >> プログラミング >> JavaScript

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を変えるだけで柔軟に集計条件を変更できるのがポイントです。

  1. JavaScriptでオブジェクトのメンバー(プロパティ・メソッド)を設定する方法

    JavaScriptでは、オブジェクトのメンバー(プロパティやメソッド)をドット記法を使って簡単に設定・追加できます。空のオブジェクトを作成した後でも、後から自由に新しいメンバーを動的に割り当てられるのがJavaScriptの柔軟な特徴です。 ここでは、実際のコード例を通じて、オブジェクトへのメンバー設定の基本を解説します。 コード例 以下は、JavaScriptでオブジェクトのメンバーを設定するサンプルコードです。 <!DOCTYPE html> <html lang=ja> <head> <meta charset=UTF-8 /> <

  2. 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 となります。つまり私たちの関数は、リスト内の各要素に対