如果值不在第二个表中的第一个表中,则Linq查询连接表

时间:2017-11-17 07:25:17

标签: c# linq join

我有两个结构如下的列表。

public class Team
{
    public string Id { get; set; }
    public Driver Driver { get; set; }
    public Driver Codriver { get; set; }
}

public class Driver
{
    public string DriverId { get; set; }
}

var modifiedTeams = new List<Team>
{
    new Team {Id = "T1", Driver = new Driver { DriverId = "D2" }, Codriver = new Driver { DriverId = "C1"} },
    new Team {Id = "T2", Driver = new Driver { DriverId = "D1"} }
};

var allTeams = new List<Team>
{
    new Team {Id = "T1", Driver = new Driver { DriverId = "D1" }, Codriver = new Driver { DriverId = "C1"} },
    new Team {Id = "T2", Driver = new Driver { DriverId = "D2"}, Codriver = new Driver { DriverId = "C2"} }
};

我希望得到来自modifiedTeams的驱动程序和codriver的所有ID,但如果在modifiedTeams中没有驱动程序/ codriver,则根据Team ID(T1,T2)从allTeams获取它。例如:来自codriver的ID T2队

Final Output:

{D2,C1,D1,C2}

有人可以帮助我如何通过C#中的LINQ(Lambda表达式)实现这一目标。

简单的C#代码: -

List<string> allDriverCodriverIds = new List<string>();
foreach (var team in modifiedTeams)
{
    if (team.Driver != null && !string.IsNullOrEmpty(team.Driver.DriverId))
    {
        allDriverCodriverIds.Add(team.Driver.DriverId);
    }
    else {
        var existingTeam = allTeams.FirstOrDefault(e => e.Id.ToString() == team.Id);
        if (existingTeam != null && existingTeam.Driver != null && !string.IsNullOrEmpty(existingTeam.Driver.DriverId))
        {
            allDriverCodriverIds.Add(existingTeam.Driver.DriverId);
        }
    }
    if (team.Codriver != null && !string.IsNullOrEmpty(team.Codriver.DriverId))
    {
        allDriverCodriverIds.Add(team.Codriver.DriverId);
    }
    else
    {
        var existingTeam = allTeams.FirstOrDefault(e => e.Id.ToString() == team.Id);
        if (existingTeam != null && existingTeam.Codriver != null && !string.IsNullOrEmpty(existingTeam.Codriver.DriverId))
        {
            allDriverCodriverIds.Add(existingTeam.Codriver.DriverId);
        }
    }
}

3 个答案:

答案 0 :(得分:1)

所以你要做的是left join allTeamsmodified小组。然后,如果左连接找到匹配的值,则获取该对象的id,否则为allTeams

var result = (from a in allTeams
             join m in modifiedTeams on a.Id equals m.Id into mj
             from m in mj.DefaultIfEmpty()
             select new Team
             {
                 Id = a.Id,
                 Driver = m?.Driver ?? a.Driver,
                 Codriver = m?.Codriver ?? a.Codriver
             }).ToList();

仅获取ID:

var result = (from a in allTeams
              join m in modifiedTeams on a.Id equals m.Id into mj
              from m in mj.DefaultIfEmpty()
              select new [] { m?.Driver?.DriverId ?? a.Driver?.DriverId, 
                              m?.Codriver?.DriverId ?? a.Codriver?.DriverId
              }).SelectMany(i => i).ToList();

答案 1 :(得分:0)

我根据你的评论编辑我的答案。请找到以下代码。

public static void KvYQ()
            {
                List<Team> modifiedTeams = new List<Team>() {
                    new Team() {
                        Id="T1"
                        ,Driver=new Driver() {
                            DriverId="D2"
                        }
                        ,Codriver=new Driver() {
                            DriverId="C1"
                        }
                    }
                    ,new Team() {
                        Id="T2"
                        ,Driver=new Driver() {
                            DriverId="D1"
                        }
                    }
                };

                List<Team> allTeams = new List<Team>() {
                    new Team() {
                        Id="T1"
                        ,Driver=new Driver() {
                            DriverId="D1"
                        }
                        ,Codriver=new Driver() {
                            DriverId="C1"
                        }
                    }
                    ,new Team() {
                        Id="T2"
                        ,Driver=new Driver() {
                            DriverId="D2"
                        }
                        ,Codriver=new Driver() {
                            DriverId="C2"
                        }
                    }
                };    

                var driverdetails = modifiedTeams.Select(x => new Team()
                {
                    Driver = x.Driver,
                    Codriver = x.Codriver

                });

                foreach (var item in driverdetails)
                {
                    Driver d = (item.Driver == null) ? allTeams.Select(x => x.Driver).FirstOrDefault() : item.Driver;
                    Driver c = (item.Codriver == null) ? allTeams.Select(x => x.Codriver).FirstOrDefault() : item.Codriver;
                    Console.WriteLine(d.DriverId + " " + c.DriverId);
                    //{D2,C1}
                    //{D1,C1}
                }


            }

您可以在一个查询中编写逻辑。但为了说清楚,我已将它写在foreach中。

希望这有帮助

答案 2 :(得分:0)

好吧,显然你有办法检查司机是否“不在场”以及CoDriver是否“不在那里”。虽然你没有指定驱动程序何时不存在,但是从我的例子中我收集到的是,如果属性等于null。

但我们假设一个团队有两个职能:

bool DriverIsThere();
bool CoDriverIsThere();

现在您有一系列TeamIds,并且您希望每个TeamId都生成两个DriverIds的{​​{1}}

如果DriversmodifiedTeam:   1如果TeamId生成IsDriverThere(),则从Driver.DriverId的{​​{1}}集合中找到Team并返回AllTeams并返回Id   2如果Driver.DriverId生成IsCodDriverThere(),则从CoDriver.DriverId的{​​{1}}集合中找到Teadm并使用相同的'Id'并生成'CoDriver.DriverId`。

如果请求AllTeams没有modifiedTeam,或者Id没有AllTeams,则您没有提及该怎么办。我们假设您要抛出异常。

此要求的此代码可以在一个LINQ语句中编写。我相信其他人会试着这样回答。作为替代方案,我想推广Yield方法,因为我认为在这种情况下它会更具可读性和可维护性。

我认为您的ID是唯一的。为了增强查找效果,我会将modifiedTeams和allTeams放入词典中,以增强查找效果。

我不想改变你的类,所以让我们使用扩展方法。

请参阅Extension Methods Demystified

让我们首先定义IsThere:

Id

根据上面定义的要求获取团队并生成两个// returns true if Driver "is there" public static bool IsThere(this Driver driver) { return driver != null; // might be for some other reason, for instance: && !driver.IsSick } 的函数非常简单:

DriverIds

更好的方法:使用委托来选择是否需要驱动程序或Codriver:

public static IEnumerable<string> ToDriverIds(this Team team,
    Dictionary<string, Team> allTeams)
{
    if (team.Driver.IsThere())
        // Driver is there, yield return the Id
        yield return team.Driver.DriverId;
    else
    {   // Driver not available, fetch the Driver from allTeams
        // throw exception if there is no alternative team with this team ID
        Team alternativeTeam = allTeams[team.Id];
        if (alternativeTeam.IsDriverThere())
            yield return alternativeTeam.Driver.Id;
        else
           // Driver also not in alternative team: exception
           throw new InvalidOperationException("...");

        // do the same for coDriver
    }
}

用法:

public static Driver ToDriver(this Team team,
    Dictionary<string, Team> allTeams,
    Func<Team, Driver> driverSelector)
{
    Driver driver = driverSelector(team);
    if (driver.IsThere())
        return driver.Id;
    else
    {   // use allTeams:
        Team allternativeTeam = allTeams[team.Id];
        driver = friverSelector(alternativeTeam);
        if (driver.IsThere())
            return driver.Id;
        else
            throw new InvalidOperationException(...);
    }
}

现在从teamId到请求的driverIds:

public static IEnumerable<string> ToDriverIds(this Team,
   Dictionary<string, Team> allTeams)
{
    // return the driverId of the driver:
    yield return Team.ToDriverId(allTeams, team => team.Driver);
    // yield return the driverId of the CoDriver:
    yield return Team.ToDriverId(allTeams, team => team.CoDriver);
}

毕竟,带有一系列teamIds,modifiedTeams和allTeams并转换为请求输出的最终函数是一个很短的函数:

public static IEnumerable<string> ToDriverIds(this string teamId,
    Dictionary<string, Team> modifiedTeams
    Dictionary<string, Team> allTeams)
{
    return modifiedTeams[teamId].ToDriverids(allTeams);
    // exception if there is no team with teamId
}