1. ホーム
  2. c#

[解決済み】LINQ - フルアウタージョイン

2022-04-07 17:29:14

質問

人のIDと名字のリストと、人のIDと姓のリストがあります。2つのリストに対して完全外部結合を行いたいと考えています。

そこで、次のようなリストを作成します。

ID  FirstName
--  ---------
 1  John
 2  Sue

ID  LastName
--  --------
 1  Doe
 3  Smith

を生成する必要があります。

ID  FirstName  LastName
--  ---------  --------
 1  John       Doe
 2  Sue
 3             Smith

私はLINQの初心者ですが、'LINQ外部結合'の解決策をいくつか発見しました。

私がこれまでに試したのは、次のようなものです。

private void OuterJoinTest()
{
    List<FirstName> firstNames = new List<FirstName>();
    firstNames.Add(new FirstName { ID = 1, Name = "John" });
    firstNames.Add(new FirstName { ID = 2, Name = "Sue" });

    List<LastName> lastNames = new List<LastName>();
    lastNames.Add(new LastName { ID = 1, Name = "Doe" });
    lastNames.Add(new LastName { ID = 3, Name = "Smith" });

    var outerJoin = from first in firstNames
        join last in lastNames
        on first.ID equals last.ID
        into temp
        from last in temp.DefaultIfEmpty()
        select new
        {
            id = first != null ? first.ID : last.ID,
            firstname = first != null ? first.Name : string.Empty,
            surname = last != null ? last.Name : string.Empty
        };
    }
}

public class FirstName
{
    public int ID;

    public string Name;
}

public class LastName
{
    public int ID;

    public string Name;
}

しかし、これが返ってくる。

ID  FirstName  LastName
--  ---------  --------
 1  John       Doe
 2  Sue

何が間違っているのでしょうか?

どうすればいいですか?

これがすべてのケースをカバーしているかどうかはわかりませんが、論理的には正しいと思われます。アイデアは、左外部結合と右外部結合を行い、その結果の和を取ることです。

var firstNames = new[]
{
    new { ID = 1, Name = "John" },
    new { ID = 2, Name = "Sue" },
};
var lastNames = new[]
{
    new { ID = 1, Name = "Doe" },
    new { ID = 3, Name = "Smith" },
};
var leftOuterJoin =
    from first in firstNames
    join last in lastNames on first.ID equals last.ID into temp
    from last in temp.DefaultIfEmpty()
    select new
    {
        first.ID,
        FirstName = first.Name,
        LastName = last?.Name,
    };
var rightOuterJoin =
    from last in lastNames
    join first in firstNames on last.ID equals first.ID into temp
    from first in temp.DefaultIfEmpty()
    select new
    {
        last.ID,
        FirstName = first?.Name,
        LastName = last.Name,
    };
var fullOuterJoin = leftOuterJoin.Union(rightOuterJoin);

これは、LINQ to Objectsにあるので、書かれている通りに動作します。LINQ to SQLなどの場合、クエリプロセッサが安全なナビゲーションや他の操作をサポートしていない可能性があります。条件演算子を使って条件付きで値を取得する必要がありますね。

すなわち

var leftOuterJoin =
    from first in firstNames
    join last in lastNames on first.ID equals last.ID into temp
    from last in temp.DefaultIfEmpty()
    select new
    {
        first.ID,
        FirstName = first.Name,
        LastName = last != null ? last.Name : default,
    };