基礎から学ぶC#入門 90日コース | 配列・コレクション・文字列 - Day 29:コレクション総合演習

C# 90日で身につけるC#
スポンサーリンク
スポンサーリンク

Day 29 のゴールと全体像

Day 29 では、これまで学んできた 配列・List・Dictionary・集計・検索 を総動員して、 「成績管理プログラム」を作ることを目標にします。

機能は次のとおりです。

  • 名前登録
  • 点数登録
  • 一覧表示
  • 平均点
  • 最高点
  • 最低点
  • 合格者表示

ここでは、コレクションをどう設計するかから始めて、 メニュー形式のコンソールアプリとして完成させるまでをステップバイステップで解説します。

成績管理プログラムのデータ構造を考える

名前と点数をどう持つか

成績管理では、

  • 名前(例:"山田太郎"
  • 点数(例:85

をペアで管理する必要があります。

このときの代表的な選択肢は次の 2 つです。

  • Dictionary<string, int>
    • キー:名前
    • 値:点数
  • List<Student>(クラスを使う)
    • Student クラスに NameScore を持たせる

Day 29 では、まずシンプルな Dictionary<string, int> を使った形で進めます。

using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        // 成績管理用の辞書:名前 → 点数
        Dictionary<string, int> scores = new Dictionary<string, int>();

        Console.ReadLine();
    }
}
C#

名前登録・点数登録の仕組み

メニューから「登録」を選ぶ流れ

成績管理プログラムは、メニュー形式で操作できるようにします。

  • 1: 成績を登録する
  • 2: 一覧表示
  • 3: 平均点表示
  • 4: 最高点表示
  • 5: 最低点表示
  • 6: 合格者表示
  • 0: 終了

まずは「登録」機能から作っていきます。

using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        Dictionary<string, int> scores = new Dictionary<string, int>();

        while (true)
        {
            Console.WriteLine();
            Console.WriteLine("=== 成績管理メニュー ===");
            Console.WriteLine("1: 成績を登録する");
            Console.WriteLine("2: 一覧表示");
            Console.WriteLine("3: 平均点表示");
            Console.WriteLine("4: 最高点表示");
            Console.WriteLine("5: 最低点表示");
            Console.WriteLine("6: 合格者表示");
            Console.WriteLine("0: 終了");
            Console.Write("番号を選んでください:");

            string? input = Console.ReadLine();

            if (input == "0")
            {
                Console.WriteLine("終了します。");
                break;
            }

            switch (input)
            {
                case "1":
                    RegisterScore(scores);
                    break;

                // 他の機能は後で追加します
                default:
                    Console.WriteLine("不正な入力です。0〜6 の番号を選んでください。");
                    break;
            }
        }
    }

    // 成績を登録するメソッド
    static void RegisterScore(Dictionary<string, int> scores)
    {
        Console.Write("名前を入力してください:");
        string? name = Console.ReadLine();

        Console.Write("点数を入力してください(0〜100):");
        string? scoreText = Console.ReadLine();

        if (string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(scoreText))
        {
            Console.WriteLine("名前と点数の両方を入力してください。");
            return;
        }

        if (!int.TryParse(scoreText, out int score))
        {
            Console.WriteLine("点数は整数で入力してください。");
            return;
        }

        if (score < 0 || score > 100)
        {
            Console.WriteLine("点数は 0〜100 の範囲で入力してください。");
            return;
        }

        if (scores.ContainsKey(name))
        {
            Console.WriteLine($"\"{name}\" さんはすでに登録されています。点数を上書きします。");
            scores[name] = score; // 上書き
        }
        else
        {
            scores.Add(name, score); // 新規登録
            Console.WriteLine($"\"{name}\" さんの点数 {score} を登録しました。");
        }
    }
}
C#

重要ポイント:

  • int.TryParse で点数入力の安全性を確保します。
  • ContainsKey で既存登録をチェックし、上書きか新規かを分けます。
  • 入力チェックを丁寧に書くことで、後のバグを減らせます。

一覧表示機能

登録済みの全員の成績を表示する

一覧表示では、Dictionary<string, int> の全要素をループで回して表示します。

// 一覧表示
static void ShowAll(Dictionary<string, int> scores)
{
    Console.WriteLine("=== 成績一覧 ===");

    if (scores.Count == 0)
    {
        Console.WriteLine("まだ成績が登録されていません。");
        return;
    }

    foreach (var entry in scores)
    {
        string name = entry.Key;
        int score = entry.Value;
        Console.WriteLine($"名前: {name}, 点数: {score}");
    }
}
C#

メニュー側にこの機能を追加します。

// switch の中に追加
case "2":
    ShowAll(scores);
    break;
C#

重要ポイント:

  • Dictionaryforeach (var entry in scores) で回すのが基本です。
  • entry.Key が名前、entry.Value が点数です。

平均点の計算

合計と件数から平均を求める

平均点は、

合計 ÷ 件数

で求めます。

// 平均点表示
static void ShowAverage(Dictionary<string, int> scores)
{
    Console.WriteLine("=== 平均点 ===");

    if (scores.Count == 0)
    {
        Console.WriteLine("成績が登録されていないため、平均を計算できません。");
        return;
    }

    int sum = 0;

    foreach (var entry in scores)
    {
        sum += entry.Value; // 点数を合計
    }

    int count = scores.Count;
    double average = (double)sum / count;

    Console.WriteLine($"人数: {count}");
    Console.WriteLine($"合計点: {sum}");
    Console.WriteLine($"平均点: {average:F2}"); // 小数第2位まで表示
}
C#

メニュー側に追加します。

case "3":
    ShowAverage(scores);
    break;
C#

重要ポイント:

  • scores.Count が件数です。
  • double にキャストしてから割り算することで、小数点付きの平均を出せます。

最高点・最低点の表示

最大値・最小値を求める

最高点(最大値)と最低点(最小値)は、 Day 27 で学んだ「最大値・最小値の求め方」と同じ考え方で求めます。

// 最高点表示
static void ShowMax(Dictionary<string, int> scores)
{
    Console.WriteLine("=== 最高点 ===");

    if (scores.Count == 0)
    {
        Console.WriteLine("成績が登録されていません。");
        return;
    }

    // 最初の要素を仮の最高点として取得
    using var enumerator = scores.GetEnumerator();
    enumerator.MoveNext();
    string maxName = enumerator.Current.Key;
    int maxScore = enumerator.Current.Value;

    // 残りの要素を比較
    foreach (var entry in scores)
    {
        if (entry.Value > maxScore)
        {
            maxScore = entry.Value;
            maxName = entry.Key;
        }
    }

    Console.WriteLine($"最高点: {maxScore} 点({maxName} さん)");
}

// 最低点表示
static void ShowMin(Dictionary<string, int> scores)
{
    Console.WriteLine("=== 最低点 ===");

    if (scores.Count == 0)
    {
        Console.WriteLine("成績が登録されていません。");
        return;
    }

    using var enumerator = scores.GetEnumerator();
    enumerator.MoveNext();
    string minName = enumerator.Current.Key;
    int minScore = enumerator.Current.Value;

    foreach (var entry in scores)
    {
        if (entry.Value < minScore)
        {
            minScore = entry.Value;
            minName = entry.Key;
        }
    }

    Console.WriteLine($"最低点: {minScore} 点({minName} さん)");
}
C#

メニュー側に追加します。

case "4":
    ShowMax(scores);
    break;

case "5":
    ShowMin(scores);
    break;
C#

重要ポイント:

  • 最初の要素を「仮の最大値/最小値」として使うのが基本です。
  • ループの中で「より大きい/より小さい」ものがあれば更新します。

合格者表示

合格ラインを決めて、条件一致で抽出する

合格者表示では、

  1. 合格ライン(例:60点以上)を決める
  2. ループで全員の点数をチェックする
  3. 条件を満たす人だけ表示する

という流れになります。

// 合格者表示
static void ShowPassed(Dictionary<string, int> scores)
{
    Console.WriteLine("=== 合格者一覧 ===");

    if (scores.Count == 0)
    {
        Console.WriteLine("成績が登録されていません。");
        return;
    }

    const int passLine = 60; // 合格ライン

    bool found = false; // 合格者が1人でもいるかどうかのフラグ

    foreach (var entry in scores)
    {
        string name = entry.Key;
        int score = entry.Value;

        if (score >= passLine)
        {
            Console.WriteLine($"名前: {name}, 点数: {score}");
            found = true;
        }
    }

    if (!found)
    {
        Console.WriteLine("合格者はいませんでした。");
    }
}
C#

メニュー側に追加します。

case "6":
    ShowPassed(scores);
    break;
C#

重要ポイント:

  • 合格ラインは定数として定義しておくと分かりやすいです。
  • フラグ(found)を使って、「合格者が1人もいない場合」のメッセージを出します。

成績管理プログラム完成版

すべての機能をまとめたコード

ここまでの機能をすべてまとめた完成版を示します。

using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        Dictionary<string, int> scores = new Dictionary<string, int>();

        while (true)
        {
            Console.WriteLine();
            Console.WriteLine("=== 成績管理メニュー ===");
            Console.WriteLine("1: 成績を登録する");
            Console.WriteLine("2: 一覧表示");
            Console.WriteLine("3: 平均点表示");
            Console.WriteLine("4: 最高点表示");
            Console.WriteLine("5: 最低点表示");
            Console.WriteLine("6: 合格者表示");
            Console.WriteLine("0: 終了");
            Console.Write("番号を選んでください:");

            string? input = Console.ReadLine();

            if (input == "0")
            {
                Console.WriteLine("終了します。");
                break;
            }

            switch (input)
            {
                case "1":
                    RegisterScore(scores);
                    break;
                case "2":
                    ShowAll(scores);
                    break;
                case "3":
                    ShowAverage(scores);
                    break;
                case "4":
                    ShowMax(scores);
                    break;
                case "5":
                    ShowMin(scores);
                    break;
                case "6":
                    ShowPassed(scores);
                    break;
                default:
                    Console.WriteLine("不正な入力です。0〜6 の番号を選んでください。");
                    break;
            }
        }
    }

    static void RegisterScore(Dictionary<string, int> scores)
    {
        Console.Write("名前を入力してください:");
        string? name = Console.ReadLine();

        Console.Write("点数を入力してください(0〜100):");
        string? scoreText = Console.ReadLine();

        if (string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(scoreText))
        {
            Console.WriteLine("名前と点数の両方を入力してください。");
            return;
        }

        if (!int.TryParse(scoreText, out int score))
        {
            Console.WriteLine("点数は整数で入力してください。");
            return;
        }

        if (score < 0 || score > 100)
        {
            Console.WriteLine("点数は 0〜100 の範囲で入力してください。");
            return;
        }

        if (scores.ContainsKey(name))
        {
            Console.WriteLine($"\"{name}\" さんはすでに登録されています。点数を上書きします。");
            scores[name] = score;
        }
        else
        {
            scores.Add(name, score);
            Console.WriteLine($"\"{name}\" さんの点数 {score} を登録しました。");
        }
    }

    static void ShowAll(Dictionary<string, int> scores)
    {
        Console.WriteLine("=== 成績一覧 ===");

        if (scores.Count == 0)
        {
            Console.WriteLine("まだ成績が登録されていません。");
            return;
        }

        foreach (var entry in scores)
        {
            Console.WriteLine($"名前: {entry.Key}, 点数: {entry.Value}");
        }
    }

    static void ShowAverage(Dictionary<string, int> scores)
    {
        Console.WriteLine("=== 平均点 ===");

        if (scores.Count == 0)
        {
            Console.WriteLine("成績が登録されていないため、平均を計算できません。");
            return;
        }

        int sum = 0;

        foreach (var entry in scores)
        {
            sum += entry.Value;
        }

        int count = scores.Count;
        double average = (double)sum / count;

        Console.WriteLine($"人数: {count}");
        Console.WriteLine($"合計点: {sum}");
        Console.WriteLine($"平均点: {average:F2}");
    }

    static void ShowMax(Dictionary<string, int> scores)
    {
        Console.WriteLine("=== 最高点 ===");

        if (scores.Count == 0)
        {
            Console.WriteLine("成績が登録されていません。");
            return;
        }

        using var enumerator = scores.GetEnumerator();
        enumerator.MoveNext();
        string maxName = enumerator.Current.Key;
        int maxScore = enumerator.Current.Value;

        foreach (var entry in scores)
        {
            if (entry.Value > maxScore)
            {
                maxScore = entry.Value;
                maxName = entry.Key;
            }
        }

        Console.WriteLine($"最高点: {maxScore} 点({maxName} さん)");
    }

    static void ShowMin(Dictionary<string, int> scores)
    {
        Console.WriteLine("=== 最低点 ===");

        if (scores.Count == 0)
        {
            Console.WriteLine("成績が登録されていません。");
            return;
        }

        using var enumerator = scores.GetEnumerator();
        enumerator.MoveNext();
        string minName = enumerator.Current.Key;
        int minScore = enumerator.Current.Value;

        foreach (var entry in scores)
        {
            if (entry.Value < minScore)
            {
                minScore = entry.Value;
                minName = entry.Key;
            }
        }

        Console.WriteLine($"最低点: {minScore} 点({minName} さん)");
    }

    static void ShowPassed(Dictionary<string, int> scores)
    {
        Console.WriteLine("=== 合格者一覧 ===");

        if (scores.Count == 0)
        {
            Console.WriteLine("成績が登録されていません。");
            return;
        }

        const int passLine = 60;
        bool found = false;

        foreach (var entry in scores)
        {
            if (entry.Value >= passLine)
            {
                Console.WriteLine($"名前: {entry.Key}, 点数: {entry.Value}");
                found = true;
            }
        }

        if (!found)
        {
            Console.WriteLine("合格者はいませんでした。");
        }
    }
}
C#

Day 29 のまとめ

Day 29 では、

  • Dictionary<string, int> を使った成績管理のデータ構造
  • 名前登録・点数登録
  • 一覧表示
  • 平均点・最高点・最低点の集計
  • 合格者表示(条件一致+フラグ管理)

という流れで、コレクションを使った総合的なプログラムを作りました。

ここまで来ると、

「データを集めて、保存して、集計して、条件で絞り込んで表示する」

という一連の流れを、自分のコードで表現できるようになっています。

この成績管理プログラムは、

  • 合格ラインを変更できるようにする
  • 複数科目に拡張する(Dictionary<string, List<int>> など)
  • 後半で学ぶ LINQ を使って、集計や並び替えをより簡潔に書き直す

といった発展の土台にもなります。

ぜひ、少しずつ機能を足したり、 自分なりのルール(例えば「80点以上は優秀者」など)を追加したりしながら、 「コレクションを使って現実の問題を解く」感覚を育てていってください。

タイトルとURLをコピーしました