読者です 読者をやめる 読者になる 読者になる

いろいろ備忘録日記

主に .NET 絡みのメモを公開しています。

Linq入門記-07 (クエリキーワード, クエリ構文, クエリ結果を一時保持, into, select, group, join)

C# Linq


今回は、intoについてです。


intoキーワードは、クエリ結果を一時保存する際に利用します。
具体的には、selectやgroup byやjoinなどのクエリ結果を保持します。


追加のクエリを更に行う際に利用します。


例を挙げると以下のような感じです。

from    x in ListX
group   x by x.Id into groupedX
orderby groupedX.Key
select  new{ .... }


上記の例では、グループ化を行った結果に対してソート処理を
行い、その後結果を構築しています。intoキーワードを使って保持しておかないと
orderbyが行えません。


意外によく利用するキーワードですね。
同じ理屈で、selectやjoinの結果も保持できます。


以下サンプルです。

#region LinqSamples-07
    public class LinqSamples07 : IExecutable {

        public class Person {
            public string   Id       { get; set; }
            public string   Name     { get; set; }
            public int      Age      { get; set; }
            public DateTime Birthday { get; set; }
        }

        public class Team {
            public string              Id      { get; set; }
            public string              Name    { get; set; }
            public IEnumerable<string> Members { get; set; }
        }

        public enum ProjectState {
             NotStarted
            ,Processing
            ,Done
        }

        public class Project {
            public string              Id      { get; set; }
            public string              Name    { get; set; }
            public IEnumerable<string> Members { get; set; }
            public DateTime            From    { get; set; }
            public DateTime?           To      { get; set; }
            public ProjectState        State   { get; set; }
        }

        // メンバー
        IEnumerable<Person> persons = 
                                new []{
                                     new Person {
                                          Id   = "001"
                                         ,Name = "gsf_zero1"
                                         ,Age  = 30
                                         ,Birthday = new DateTime(1979, 11, 18)
                                     }
                                    ,new Person {
                                          Id   = "002"
                                         ,Name = "gsf_zero2"
                                         ,Age  = 20
                                         ,Birthday = new DateTime(1989, 11, 18)
                                     }
                                    ,new Person {
                                          Id   = "003"
                                         ,Name = "gsf_zero3"
                                         ,Age  = 18
                                         ,Birthday = new DateTime(1991, 11, 18)
                                    }
                                    ,new Person {
                                          Id   = "004"
                                         ,Name = "gsf_zero4"
                                         ,Age  = 40
                                         ,Birthday = new DateTime(1969, 11, 18)
                                    }
                                    ,new Person {
                                          Id   = "005"
                                         ,Name = "gsf_zero5"
                                         ,Age  = 44
                                         ,Birthday = new DateTime(1965, 11, 18)
                                    }
                                };

        // チーム
        IEnumerable<Team> teams = 
                                new []{
                                     new Team {
                                          Id      = "001"
                                         ,Name    = "team_1"
                                         ,Members = new []{ "001", "004" }
                                     }
                                    ,new Team {
                                          Id      = "002"
                                         ,Name    = "team_2"
                                         ,Members = new []{ "002", "003" }
                                     }
                                };

        // プロジェクト
        IEnumerable<Project> projects = 
                                new []{
                                     new Project {
                                          Id      = "001"
                                         ,Name    = "project_1"
                                         ,Members = new []{ "001", "002" }
                                         ,From    = new DateTime(2009, 8, 1)
                                         ,To      = new DateTime(2009, 10, 15)
                                         ,State   = ProjectState.Done
                                     }
                                    ,new Project {
                                          Id      = "002"
                                         ,Name    = "project_2"
                                         ,Members = new []{ "003", "004" }
                                         ,From    = new DateTime(2009, 9, 1)
                                         ,To      = new DateTime(2009, 11, 25)
                                         ,State   = ProjectState.Done
                                     }
                                    ,new Project {
                                          Id      = "003"
                                         ,Name    = "project_3"
                                         ,Members = new []{ "001" }
                                         ,From    = new DateTime(2007, 1, 10)
                                         ,To      = null
                                         ,State   = ProjectState.Processing
                                     }
                                    ,new Project {
                                          Id      = "004"
                                         ,Name    = "project_4"
                                         ,Members = new []{ "001", "002", "003", "004" }
                                         ,From    = new DateTime(2010, 1, 5)
                                         ,To      = null
                                         ,State   = ProjectState.NotStarted
                                     }
                                };

        public void Execute() {
            //
            // intoのサンプル
            // intoはselect, group, joinの結果を一時的に保持できる。
            //
            // 下記のサンプルの場合、groupの結果をintoで保持してから
            // ソート処理を行い、結果を構築している。(追加のクエリ処理)
            //
            // 各年齢層のカウントを算出.
            //
            var query1 = from    person in persons
                         group   person by (Math.Truncate((person.Age * 0.1)) * 10) into personByAge
                         orderby personByAge.Key ascending
                         select new { 
                              AgeLevel = personByAge.Key
                             ,Count    = personByAge.Count()
                         };

            foreach(var item in query1) {
                //
                // 匿名型は、内部で自動的にToStringが作成されているので
                // そのまま、ToStringを呼べばプロパティの値が表示される。
                //
                Console.WriteLine(item);
            }

            //
            // 上記の場合は、最後に匿名型を構築して
            // クエリ結果としているが、当然そのままIGroupingの結果で
            // 返却しても同じ結果が導ける.
            //
            var query2 = from    person in persons
                         group   person by (Math.Truncate((person.Age * 0.1)) * 10) into personByAge
                         orderby personByAge.Key ascending
                         select  personByAge;

            foreach(var item in query2) {
                Console.WriteLine("Age={0}, Count={1}", item.Key, item.Count());
            }
        }

    }
#endregion


余談ですが、SQLの場合と同じくグループ化を行った後は
コンテキストが変わりますので、上記のサンプルを

            var query1 = from    person in persons
                         group   person by (Math.Truncate((person.Age * 0.1)) * 10) into personByAge
                         orderby personByAge.Key ascending
                         select new { 
                              PersonId = person.Id
                             ,AgeLevel = personByAge.Key
                             ,Count    = personByAge.Count()
                         };

のように、selectの中にグループ化される前のperson.Idを含めるとコンテキストが違うという
エラーが発生します。この辺はSQLを作成する際と同じ注意が必要です。