いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Linq入門記-30 (LINQ To Object, GroupJoin)


今回は、GroupJoin拡張メソッドについてです。


GroupJoin拡張メソッドは、クエリ式での「join xx in xx on xx equals xx into xx」に対応する拡張メソッドです。
LINQでのグループ結合については、以前の記事で記述していますので、そちらを参照下さればと思います。


GroupJoin拡張メソッドの書式は以下の通りです。

public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(
    this IEnumerable<TOuter> outer,
    IEnumerable<TInner> inner,
    Func<TOuter, TKey> outerKeySelector,
    Func<TInner, TKey> innerKeySelector,
    Func<TOuter, IEnumerable<TInner>, TResult> resultSelector
)

public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(
    this IEnumerable<TOuter> outer,
    IEnumerable<TInner> inner,
    Func<TOuter, TKey> outerKeySelector,
    Func<TInner, TKey> innerKeySelector,
    Func<TOuter, IEnumerable<TInner>, TResult> resultSelector,
    IEqualityComparer<TKey> comparer
)


一つ目の書式が一番よく利用される形のものですね。
2つめの書式は、一つ目の分にIEqualityComparer comparerが追加されています。
キーの同値性を独自の方法で決定する際に利用します。


引数が多いですが、慣れるとそんなに難しくありません。
クエリ式で

from team   in teams
join person in people on team equals person.Team into personCollection
select new { Team = team, Persons = personCollection }


という記述は、

teams.GroupJoin(people, team => team, person => person.Team, (team, personCollection) => new { Team = team, Persons = personCollection })


となります。


Join拡張メソッドとほとんど同じなのですが、こちらは階層構造を構築してくれます。
つまり、TOuter(上記の例でいうとTeamオブジェクト)単位に、結合されたTInnerのシーケンス(上記の例でいうとPersonオブジェクト)が
がくっつく形になります。


[Join拡張メソッドの場合]

Team Person
t1 p1
t1 p2
t2 p3


[GroupJoin拡張メソッドの場合]

  • t1
    • p1
    • p2
  • t2
    • p3

のイメージです。


以下、サンプルです。

    #region LinqSamples-27
    public class LinqSamples27 : IExecutable
    {
        class Person
        {
            public string Name { get; set; }
            public Team   Team { get; set; }
        }
        
        class Team
        {
            public string Name { get; set; }
        }

        public void Execute()
        {
            var t1 = new Team { Name = "Team 1" };
            var t2 = new Team { Name = "Team 2" };
            
            var p1 = new Person { Name = "gsf_zero1", Team = t1 };
            var p2 = new Person { Name = "gsf_zero2", Team = t2 };
            var p3 = new Person { Name = "gsf_zero3", Team = t1 };
            
            var teams  = new List<Team>   { t1, t2 };
            var people = new List<Person> { p1, p2, p3 };
            
            //
            // グループ結合する.
            // 
            // Join拡張メソッドと書式的にはほとんど同じであるが、以下の点が異なる。
            //    ・resultSelectorの書式が、(TOuter, IEnumerable<TInner>)となっている。
            // これにより、結果をJOINした結果をグルーピングした状態で保持することが出来る。(階層構造を構築出来る。)
            //
            // 以下のクエリ式と同じ事となる。
            //    from team   in teams
            //    join person in people on team equals person.Team into personCollection
            //    select new { Team = team, Persons = personCollection }
            //
            var query = teams.GroupJoin                 // TOuter
                        (
                            people,                     // TInner
                            team   => team,             // TOuterのキー
                            person => person.Team,      // TInnerのキー
                            (team, personCollection) => // 結果 (TOuter, IEnumerable<TInner>)
                                new { Team = team, Persons = personCollection }
                        );
            
            foreach (var item in query2)
            {
                Console.WriteLine("TEAM = {0}", item.Team.Name);
                foreach (var p in item.Persons)
                {
                    Console.WriteLine("\tPERSON = {0}", p.Name);
                }
            }
        }
    }
    #endregion


実行結果は以下の通りです。

  TEAM = Team 1
    PERSON = gsf_zero1
    PERSON = gsf_zero3
  TEAM = Team 2
    PERSON = gsf_zero2

================================
過去の記事については、以下のページからご参照下さい。