using System;
using System.Collections.Generic;
using System.Linq;
public static class EnumerableMapReduce
{
///
/// MapReduce 擴充方法
///
/// 輸入元素型別
/// 分組鍵型別
/// Map 輸出值型別
/// Reduce 結果型別
/// 資料來源
/// Map 函式:單一輸入 → 多組 Key-Value
/// Reduce 函式:Key + Values 序列 → 結果
/// Key → Result 字典
public static Dictionary MapReduce(
this IEnumerable source,
Func>> map,
Func, TResult> reduce)
{
// Phase 1: Map + Group (Shuffle)
var grouped = new Dictionary>();
foreach (var item in source)
{
foreach (var kv in map(item))
{
if (!grouped.TryGetValue(kv.Key, out var list))
{
list = new List();
grouped[kv.Key] = list;
}
list.Add(kv.Value);
}
}
// Phase 2: Reduce
var result = new Dictionary();
foreach (var g in grouped)
{
result[g.Key] = reduce(g.Key, g.Value);
}
return result;
}
}
因為單一輸入可能產生多組 Key-Value (如單一文檔拆分多個單字),需用 yield return 逐一產出。Lambda 無法使用 yield,故 Map 需為獨立方法或區塊。
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
static void Main()
{
var people = new List
{
new Person { Id = 1, Name = "user1", Age = 23 },
new Person { Id = 2, Name = "user2", Age = 24 },
new Person { Id = 3, Name = "user3", Age = 23 },
new Person { Id = 4, Name = "user4", Age = 25 },
new Person { Id = 5, Name = "user5", Age = 20 }
};
// 只處理年齡 > 22 的人
// Map: 年齡 → 人名
// Reduce: 將同齡人名用逗號連接
var result = people.MapReduce(
MapByAge,
(age, names) => string.Join(",", names)
);
foreach (var kv in result.OrderBy(x => x.Key))
Console.WriteLine($"{kv.Key}: {kv.Value}");
}
// Map 方法:使用 yield return 產生多組結果
static IEnumerable> MapByAge(Person p)
{
if (p.Age > 22)
yield return new KeyValuePair(p.Age, p.Name);
}
23: user1,user3
24: user2
25: user4
public static Dictionary MapReduceParallel(
this IEnumerable source,
Func>> map,
Func, TResult> reduce)
{
// Parallel Map + Group (使用 ConcurrentDictionary)
var grouped = new System.Collections.Concurrent.ConcurrentDictionary>();
source.AsParallel().ForAll(item =>
{
foreach (var kv in map(item))
{
grouped.AddOrUpdate(kv.Key,
_ => new List { kv.Value },
(_, list) => { lock (list) list.Add(kv.Value); return list; });
}
});
// Reduce (可平行)
var result = new Dictionary();
foreach (var g in grouped)
result[g.Key] = reduce(g.Key, g.Value);
return result;
}
| 特性 | MapReduce | GroupBy |
|---|---|---|
| 單一輸入 → 多 Key | 原生支援 (Map 回傳多組) | 需 SelectMany + GroupBy |
| 語意明確 | Map/Reduce 關注點分離 | 單一查詢表達式 |
| 擴充性 | 易擴展為分散式 (Hadoop/Spark) | 記憶體內單機 |
| 效能 | 手動優化空間大 | 高度優化 |
這個擴充方法將 MapReduce 模式帶入 C# LINQ 生態,讓開發者能用熟悉的語法表達分散式計算邏輯,是從單機邁向大數據處理的優秀踏腳石。