[C#] ToLookup, GroupBy, ToDictionary簡單介紹

[C#] ToLookup, GroupBy, ToDictionary簡單介紹

前言

在我們將資料進行分組,

或是做一些Key => Value Mapping的動作時,

我們會經常使用到LINQ這三個方法,

而在ToLookup, GroupBy, ToDictionary這三個方法中,

ToLookup和GroupBy是比較接近的,

兩者都是將資料分組的動作,

而ToDictionary則是可以方便我們將資料的某個欄位抽取出來當作Key值。

實際演練

而既然ToLookup和GroupBy兩者都是將資料做分組,

那它們的本質上有什麼不同呢?

答案是GroupBy會 延遲執行 (Deffered Execution)

讓我們來看看範例說明。
 

我們先建立一個假資料的來源,模擬讀取資料庫。


        public static List<Person> GetPersonList()
        {
            return new List<Person>()
            {
                new Person(){ID=1,Name="Kirk", Address="aaaa", Birthday=new DateTime(1985,6,8), Introduction="aaaa", Phone="0927896456",Group="A"},
                new Person(){ID=2,Name="John", Address="bbbb", Birthday=new DateTime(1972,1,3), Introduction="aaaa", Phone="0927111111",Group="A"},
                new Person(){ID=3,Name="David", Address="cccc", Birthday=new DateTime(1964,7,13), Introduction="aaaa", Phone="0927222222",Group="B"},
                new Person(){ID=4,Name="Steven", Address="dddd", Birthday=new DateTime(1982,6,1), Introduction="aaaa", Phone="0927333333",Group="B"},
                new Person(){ID=5,Name="Jason", Address="eeee", Birthday=new DateTime(1983,10,3), Introduction="aaaa", Phone="0927444444",Group="A"},
                new Person(){ID=6,Name="Titan", Address="ffff", Birthday=new DateTime(1987,7,2), Introduction="aaaa", Phone="0927555555",Group="C"},
                new Person(){ID=7,Name="Sophia", Address="gggg", Birthday=new DateTime(1981,3,27), Introduction="aaaa", Phone="0927666666",Group="A"},
                new Person(){ID=8,Name="Mary", Address="hhhh", Birthday=new DateTime(1982,12,18), Introduction="aaaa", Phone="0927777777",Group="D"},
                new Person(){ID=9,Name="Tom", Address="iiii", Birthday=new DateTime(1983,9,23), Introduction="aaaa", Phone="0927888888",Group="B"},
                new Person(){ID=10,Name="Julia", Address="jjjj", Birthday=new DateTime(1975,3,18), Introduction="aaaa", Phone="0927999999",Group="A"},
            };
        }


首先我們來看看ToLookup的範例


              var personList = GetPersonList();
            var personListLookUp = personList.ToLookup(i => i.Group); // Execute query immediately

            personList.RemoveAll(i => true);

            foreach (var personGroup in personListLookUp)
            {
                Console.WriteLine("Group: {0}", personGroup.Key);

                foreach (var person in personGroup)
                {
                    Console.WriteLine("ID: {0}, Name: {1}", person.ID, person.Name);
                }
            }

執行結果:

Group: A
ID: 1, Name: Kirk
ID: 2, Name: John
ID: 5, Name: Jason
ID: 7, Name: Sophia
ID: 10, Name: Julia
Group: B
ID: 3, Name: David
ID: 4, Name: Steven
ID: 9, Name: Tom
Group: C
ID: 6, Name: Titan
Group: D
ID: 8, Name: Mary
 

我們可以發現,就算我們將personList中的資料都移除了,

也不會影響ToLookup所取得的資料,因為所有的資料都在ToLookup時就已經讀取完畢,

並回傳儲存至personListLookUp中了。
 

再來我們看看GroupBy的範例


            var personList = GetPersonList();
            var personListGroupBy = personList.GroupBy(i => i.Group); // Do not execute query

            personList.RemoveAll(i => true);

            foreach (var personGroup in personListGroupBy) // Do query now
            {
                Console.WriteLine("Group: {0}", personGroup.Key);

                foreach (var person in personGroup)
                {
                    Console.WriteLine("ID: {0}, Name: {1}", person.ID, person.Name);
                }
            }

會發現執行結果唯一空集合,

這是因為GroupBy會一直等到第一次去讀取資料內容時才執行Query,

而在那之前,原始資料已被清空了,自然就不會有任何結果,

這也是LINQ的一個叫做延遲執行的特性所造成的,

它可以方便我們在確認組完所有的Condition之後,再執行Query,以增進效能,

而在實際上使用時,我們也可根據使用情境的不同,來選擇要使用ToLookup或GroupBy

但在使用時就必須要瞭解它,來避免執行結果不是預期中的囉!
 

最後在附上一個ToDictionary的範例,


            var personList = GetPersonList();
            var personListDictionary = personList.ToDictionary(i => i.ID);

            personList.RemoveAll(i => true);

            foreach (var person in personListDictionary)
            {
                Console.WriteLine("ID: {0}, Name: {1}", person.Key, person.Value.Name);
            }

執行結果:

ID: 1, Name: Kirk
ID: 2, Name: John
ID: 3, Name: David
ID: 4, Name: Steven
ID: 5, Name: Jason
ID: 6, Name: Titan
ID: 7, Name: Sophia
ID: 8, Name: Mary
ID: 9, Name: Tom
ID: 10, Name: Julia

 

結語

LINQ 是.Net Framework 3.5所增加的一個十分強大的新功能,

在熟悉了它之後幾乎無法忘掉它所帶來的種種好處,

但在享受它好處的同時,也不要忘記去理解它的一些原理,

以避免不小心掉入某些陷阱而不自知囉!

若以上說明有任何的問題或指教,還請大家多多包涵提出討論 ^^

 

參考連結:

  1. LINQ and Deferred Execution
  2. C# 中奇妙的函數 -- 1. ToLookup