iT邦幫忙

DAY 19
4

分享一些學習心得系列 第 19

LINQ自學筆記-語法應用-取出資料-SelectMany 運算子

  • 分享至 

  • xImage
  •  

有了前一篇 Select 運算子的基礎,這篇文章將和大家分享 SelectMany 運算子,它是一開始接觸 LINQ 的朋友很不容易了解的方法,但其實它並不難懂,本文會用簡單的資料來源做 Select 和 SelectMany 比較,讓大家更易於了解和應用。
自學筆記這系列是我自己學習的一些心得分享,歡迎指教。這系列的分享,會以 C# + 我比較熟的 Net 3.5 環境為主。
另外本系列預計至少會切成【打地基】和【語法應用】兩大部分做分享。打地基的部分,講的是 LINQ 的組成元素,這部分幾乎和 LINQ 無關,反而是 C# 2.0、C# 3.0 的一堆語言特性,例如:型別推斷、擴充方法、泛型、委派等等,不過都會把分享的範圍限制在和 LINQ 應用有直接相關功能。
PS. LINQ 自學筆記幾乎所有範例,都可直接複製到 LINQPad 4 上執行(大多是用 Statements 和 Program 模式)。因為它輕巧好用,功能強大,寫範例很方便,請大家自行到以下網址下載最新的 LINQPad:http://www.LINQpad.net/。
SelectMany 運算子會先做和 Select 相同的事,也就是從資料來源序列中,根據我們所設定的條件,建立輸出序列,但是差異是,Select 運算子只要取得第一層的查詢結果就會放到輸出序列中,但是 SelectMany 若發現查詢結果回傳的是一個可以列舉的序列,則會再進一步把這個序列中的項目取出來,放到輸出序列中,也就是用 SelectMany 運算子,查詢出來的輸出序列之深度會比 Select 少一層(若 Select 結果是三層,用 SelectMany 會只有兩層)。

用文字說明很難懂,開發人員最喜歡看 Code 了,所以範例如下:

string[] ary = new string[]{"zero", "one", "two", "three"}; 
var querySelect = ary.Select(a => a); 
var querySelectMany = ary.SelectMany(a => a); 
querySelect.Dump("Select result", false);
querySelectMany.Dump("SelectMany result", false);

上述程式碼非常簡單,就一個字串陣列,然後分別用 Select 和 SelectMany 運算子取回查詢結果,完全沒有使用其他查詢運算子喔,那大家猜猜查詢結果會相同嗎?請看:

上圖是 Select 運算子的查詢結果,應該完全不意外,就是四個項目,但是 SelectMany 可就出乎意料囉:

SelectMany 對這個字串陣列的查詢結果變成 IEnumerable<Char> 了!原因為何?請先看一下摘錄自 MSDN 對於 String 類別說明:
字串是指用來表示文字的 Unicode 字元之循序集合。String 物件是指表示字串的 System.Char 物件之循序集合。

也就是說,每個字串(String)類別其實是一個字元(Char)陣列(循序集合),也就是字串是【可以列舉】的序列,正好符合 SelectMany 發現查詢結果若是可以列舉的序列,就會把這個序列攤平之定義,所以每個字串都被拆成字元了。

這個程式碼再變化一下:

string[] ary = new string[]{"zero", "one", "two", "three"}; 
var querySelect = ary.Select(a => a.Split('r'));
var querySelectMany = ary.SelectMany(a => a.Split('r'));
querySelect.Dump("Select result", false);
querySelectMany.Dump("SelectMany result", false);

同樣的資料來源(字串陣列),我們在查詢時用 Lambda 運算式,把每個項目用字元「r」 拆成新的子陣列,所以 Select 的查詢結果,應該是一個 IEnumerable<String[]>,也就深度為 3 的輸出序列:

但是改用 SelectMany,就變成深度為 2 的 IEnumerable<String> 囉:

注意,因為字串本身是字元陣列,所以上述雖然看起來是深度 2(Select result) 和 1(SelectMany result),但精確的講應該是深度 3 和 2,。

同一個資料來源,再來一個進階變化:

string[] ary = new string[]{"zero", "one", "two", "three"};
var querySelect = ary.Select(a => (a.Split('r')).Select(x => x.Split('n')));
var querySelectMany = ary.SelectMany(a => (a.Split('r')).Select(x => x.Split('n')));
querySelect.Dump("Select result", false);
querySelectMany.Dump("SelectMany result",false);

如果這個案例大家能講出答案,那應該 SelectMany 運算子的應用就算是熟練了!看看查詢結果:


上面是 Select 的結果(因為超過抓圖範圍,所以切兩張圖),深度多少我就不講了,大家應該可以算的出來。
接著是 SeletMany 的結果,應該不意外囉:


上一篇
LINQ自學筆記-語法應用-取出資料-Select 運算子
下一篇
LINQ自學筆記-語法應用-篩選資料-Where 運算子
系列文
分享一些學習心得30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言