C# 動態依照資料產生報表教學

在上一篇 C# WinForm 動態產生 DataGridView,動態新增資料 手把手教學 中點閱率還不錯,只是沒有人留言,看來大家都學得很成功,都沒有什麼問題...😅,於是再來教點進階的 —— 關於樞紐分析表

最近我們公司在徵才,其中一題面試的題目簡化後是這樣的:

傳入資料:
姓名 科目 考試類型 分數
小花 國文 期中考 68
小花 英文 期中考 77
小花 數學 期中考 87
小花 物理 期中考 66
小王 國文 期中考 45
小王 英文 期中考 50
小王 數學 期中考 78
小王 物理 期中考 63
小陳 國文 期中考 75

將上述資料整理後輸出為下表:
姓名 國文 英文 數學 物理
小花 68 77 87 66
小王 45 50 78 63
小陳 75 0 0 0

其實非常簡單,於是就來帶大家實作一次,首先先想辦法將資料放到程式內:先建立物件
    

public sealed class Test
{
    public string Student { get; set; }
    public string Course { get; set; }
    public string ExamType { get; set; }
    public int Score { get; set; }
}
    

放入資料:
    

public static List<Test> Tests = new()
{
    new Test { Student = "小花", Course = "國文", ExamType = "期中考", Score = 68 },
    new Test { Student = "小花", Course = "英文", ExamType = "期中考", Score = 77 },
    new Test { Student = "小花", Course = "數學", ExamType = "期中考", Score = 87 },
    new Test { Student = "小花", Course = "物理", ExamType = "期中考", Score = 66 },
    new Test { Student = "小王", Course = "國文", ExamType = "期中考", Score = 45 },
    new Test { Student = "小王", Course = "英文", ExamType = "期中考", Score = 50 },
    new Test { Student = "小王", Course = "數學", ExamType = "期中考", Score = 78 },
    new Test { Student = "小王", Course = "物理", ExamType = "期中考", Score = 63 },
    new Test { Student = "小陳", Course = "國文", ExamType = "期中考", Score = 75 },
};
    

好了,簡單的做完了,那接下來該怎麼辦呢?其實也不難,只要將資料依照姓名群組起來,再排列順序,並把沒有的填入預設值(補 0)即可,如果你熟悉 LINQ,那應該知道群組就是使用 GroupBy,然後再使用匿名類別將得到的資料整理一下,就差不多了。
延伸閱讀:C# 使用 GroupBy 將資料分組

程式碼:
    

var list = Tests
    .Where(x => x.ExamType == "期中考")
    .GroupBy(x => x.Student)
    .Select(x =>
        new
        {
            Student = x.Key,
            國文 = x.Where(y => y.Course == "國文").Select(y => y.Score).FirstOrDefault(),
            英文 = x.Where(y => y.Course == "英文").Select(y => y.Score).FirstOrDefault(),
            數學 = x.Where(y => y.Course == "數學").Select(y => y.Score).FirstOrDefault(),
            物理 = x.Where(y => y.Course == "物理").Select(y => y.Score).FirstOrDefault()
        }
    )
    .ToList();

list.ForEach(x =>Console.WriteLine($"姓名: {x.Student}, 國文: {x.國文}, 英文: {x.英文}, 數學: {x.數學}, 物理: {x.物理}"));
    

輸出訊息:
    

姓名: 小花, 國文: 68, 英文: 77, 數學: 87, 物理: 66
姓名: 小王, 國文: 45, 英文: 50, 數學: 78, 物理: 63
姓名: 小陳, 國文: 75, 英文: 0, 數學: 0, 物理: 0
    

不過到這裡先暫停一下,輸出是能輸出,不過寫太死了,也有很多重複的程式碼,其實進階一點我們可以讓「科目」是動態的,假設今天還沒考國文,總不可能大家國文都先填入 0 分把?正常情況下應該是會空白或是沒有這個科目的欄位,所以我們先找到所有科目名稱然後去重複,動態決定要顯示哪些欄位:
    

List<string> columns = Tests.Select(x => x.Course).Distinct().ToList(); // 所有科目名稱
    

接下來就是建立資料表和設定欄位:
    

DataTable dataTable = new();

List<string> columns = Tests.Select(x => x.Course).Distinct().ToList(); // 所有科目名稱
dataTable.Columns.Add("姓名");
columns.ForEach(x => dataTable.Columns.Add(x));
    

將每個學生的成績依照欄位填入,並加入到資料表中:
    

Tests
    .Where(x => x.ExamType == "期中考")
    .GroupBy(x => x.Student)
    .Select(x =>
        {
            var row = dataTable.NewRow();
            row[0] = x.Key;
            foreach (var test in x)
            {
                row[test.Course] = test.Score;
            }

            return row;
        }
    ).ToList()
    .ForEach(x => dataTable.Rows.Add(x));
    

動態建立 DataGridView 和係結資料表:

DataGridView dataGridView = new DataGridView();
this.Controls.Add(dataGridView); // 新增到當前的 Form 中
dataGridView.Dock = DockStyle.Fill; // 填滿視窗
dataGridView.DataSource = dataTable;
    

延伸閱讀:C# WinForm 動態產生 DataGridView,動態新增資料教學

執行結果:

嗯,看起來 100 分!

留言