Image

當旋律悄然劃過時空的軌跡,每一首歌就像一顆星辰,閃耀著創作者獨有的靈魂。
你是否曾想過,這些音符與樂章之間,是否也隱藏著某種秩序?某種——相似而未被察覺的連結

在現實中,我們以風格定義音樂,以性別分類聲線,以名字尋找熟悉。
在程式語言的世界中,我們用 GroupBy 尋找規律,試圖透過「分組」理解資料之間微妙的關係。這不只是邏輯的操作,更像是一場哲學的提問:


         什麼定義了我們的「相同」?

     在群體之中,我們究竟選擇呈現什麼?又忽略了什麼?


今天,讓我們從一份歌手與歌曲的資料出發,探索 LINQ 中 GroupBy 的靈魂。






MS 文件中的命名解析

官方文件連結

交響樂需要指揮的手勢,GroupBy 也仰賴幾個核心角色來指引分組的方式與結果

  • keySelector

    它定義了分組的依據 —— 像是將歌曲按照曲風(搖滾、爵士、古典)、調性(大調、小調)、或是演出者進行分類。每一個 key 就像樂章中的主題,決定了整體的情緒。

  • comparer

    當我們決定依照某個欄位分組後,還需要定義「何謂相同」。
    Adoado 這兩個名字?還是說它們應被視為不同?這就像辨識旋律中的細微差異——不同演奏者的詮釋是否屬於同一首曲子?

  • elementSelector

    分好組之後,我們要選擇的是:組內要呈現什麼?
    是整筆資料、只顯示歌曲名?還是演出者的性別?這像是在決定觀眾到底看哪一段表演。

  • resultSelector

    如果說 elementSelector 是選擇每段表演的內容,resultSelector 則是決定整場演出的總呈現
    或許我們不只要知道有哪些歌曲,而是想知道每位歌手的代表作、總共幾人參與,或某組的平均年齡——這些都是原始資料中未直接給出的價值。






讓資料說話

我們有這樣一份資料,來自世界各地的音樂人…

1
2
3
4
5
6
7
8
9
10
11

var singers = new List<Singer>
{
new Singer { Name = "Ado", Genre = "J-Pop", Song = "Lemon", IsFemale = true },
new Singer { Name = "ado", Genre = "J-Pop", Song = "Usseewa", IsFemale = true },
new Singer { Name = "Taylor Swift", Genre = "Pop", Song = "Shake It Off", IsFemale = true },
new Singer { Name = "Ed Sheeran", Genre = "Pop", Song = "Shape of You", IsFemale = false },
new Singer { Name = "Beethoven", Genre = "Classical", Song = "Für Elise", IsFemale = false },
new Singer { Name = "Miles Davis", Genre = "Jazz", Song = "Kind of Blue", IsFemale = false }
};

elementSelector / comparer

若我們希望將相同名字的歌手歸為一組,即便大小寫不同也視為相同人,可使用 StringComparer.OrdinalIgnoreCase

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
	var groupedBySingerName = singers.GroupBy(
s => s.Name,
StringComparer.OrdinalIgnoreCase
);

foreach (var group in groupedBySingerName)
{
Console.WriteLine($"Group Key: {group.Key}");
foreach (var singer in group)
{
Console.WriteLine($"- {singer.Name} - {singer.Song}");
}
Console.WriteLine();
}


/// Group Key: Ado
/// - Ado - Lemon
/// - ado - Usseewa

/// Group Key: Taylor Swift
/// - Taylor Swift - Shake It Off


resultSelector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   // 使用 resultSelector
var groupedBySingerGender = singers.GroupBy(
s => s.IsFemale,
(key,group) => new{
Gender = key ? "Female" : "Male",
Count = group.Count(),
Songs = group.Select(s => s.Song)
}
);

// 輸出每個組別的性別、人數和歌曲列表
foreach (var group in groupedBySingerGender)
{
Console.WriteLine($"Gender: {group.Gender}");
Console.WriteLine($"Singer Count: {group.Count}");
Console.WriteLine("Songs:");
foreach (var song in group.Songs)
{
Console.WriteLine($"- {song}");
}
Console.WriteLine();
}

用 GroupBy 看見背後的故事

在這個例子中,GroupBy 不只幫助我們將資料歸類,它也讓我們重新思考:如何定義差異,如何尋找共通,如何呈現意義。召喚我們看見被忽略的關聯、遺落的故事,以及那個「屬於同一組」的哲學命題。