【问题标题】:Can I select multiple objects in a Linq query我可以在 Linq 查询中选择多个对象吗
【发布时间】:2009-02-06 15:45:21
【问题描述】:

我可以在一个选择中返回多个项目吗?例如,我有一个固定装置列表(想想足球(或美式足球)固定装置)。每个固定装置包含一个主客队和一个主客场比分。我想得到所有平局的球队。我想使用类似的东西

IEnumerable<Team> drew = from fixture in fixtures
                         where fixture.Played && (fixture.HomeScore == fixture.AwayScore)
                         select fixture.HomeTeam && fixture.AwayTeam;

我知道这种语法不正确,我不知道是否可以这样做。我需要两个查询然后将它们连接起来吗?

编辑:这确实是一门学习的事情,因此以任何特定方式实现这一点并不重要。基本上,在这个阶段,我想要的只是一个已经抽签的球队名单。一个示例用法可能是,对于给定的赛程列表,我可以找到所有被抽签的球队,这样我就可以将他们在表格中的排名更新 1 分(赢 3 分,输 0 分)。

【问题讨论】:

    标签: c# linq list


    【解决方案1】:

    101 LINQ Samples,即Select - 匿名类型1

    ... select new { HomeTeam = fixture.HomeTeam, AwayTeam = fixture.AwayTeam };
    

    【讨论】:

    • 不是他要找的答案。他想要一个团队列表,而不是具有主队和客队属性的匿名类型列表。
    • 这是真的...我可以使用匿名类型绕过它...只是想知道是否有办法只获取团队列表。如果这是唯一的方法,那也是唯一的方法
    • 我同意这不会返回团队列表,但我认为他最好调整他的代码以支持处理这种匿名类型。如果 James Hay 可以更新他的问题以描述他的用法可能会有所帮助。
    • 我认为他的问题已经完美地描述了他的要求:“我想要一份抽签的球队名单。”他可能不想在这里使用匿名类型有很多原因(需要在此方法之外传递列表是一个常见的原因)。
    • 自原始帖子以来,元组已添加到 .Net/C# 中,并且在许多可以使用匿名类型的地方可能是一个选项。 blogs.msdn.microsoft.com/mazhou/2017/05/26/…
    【解决方案2】:

    以下将返回一个 IEnumerable

    IEnumerable<Team> drew =
        from fixture in fixtures
        where fixture.Played && (fixture.HomeScore == fixture.AwayScore)
        from team in new[]{fixture.HomeTeam, fixture.AwayTeam}
        select team;
    

    或者,使用流畅的 LINQ 风格:

    IEnumerable<Team> drew =
        fixtures
        .Where(fxtr => fxtr.Played && (fxtr.HomeScore == fxtr.AwayScore))
        .SelectMany(fixture => new[]{fixture.HomeTeam, fixture.AwayTeam});
    

    扁平化和平面地图

    此要求通常称为“扁平化”。也就是说,获取一个 > 并将其转换为一个

    SelectMany 既映射(一个固定到一个团队的阵列)和展平(一个团队阵列的序列到一个团队的序列)。类似于Java、JavaScript等其他语言中的“flatMap”功能。

    可以将 Mapping 和 Flattening 分开:

    IEnumerable<Team> drew =
        fixtures
        .Where(fxtr => fxtr.Played && (fxtr.HomeScore == fxtr.AwayScore))
        // map 
        .Select(fixture => new[]{fixture.HomeTeam, fixture.AwayTeam})
        // flatten
        .SelectMany(teams => teams);
    

    其他方法

    迭代器块

    使用迭代器块也可以达到同样的效果,但我怀疑这很少是最好的方法:

    IEnumerable<Team> Drew(IEnumerable<Fixture> fixtures){
        var draws = 
          fixtures
          .Where(fxtr => fxtr.Played && (fxtr.HomeScore == fxtr.AwayScore));
    
        foreach(var fixture in draws){
            yield return fixture.HomeTeam;
            yield return fixture.AwayTeam;
        }
    }
    

    联合

    联合也是一种选择,但有可能产生与上述不同的结果:

    1. 结果的顺序会有所不同。返回所有 Home 结果,然后返回所有 Away 结果。

    2. Union 枚举了两次固定装置,因此,根据固定装置的实现方式,固定装置可能会在调用之间更新。例如,如果在通话之间添加了新的抽签赛程,则可以返回客队,但不能返回主队。

    正如迈克鲍威尔所描述的:

    IEnumerable<Team> drew =
        ( from fixture in fixtures
          where fixture.Played && (fixture.HomeScore == fixture.AwayScore)
          select fixture.HomeTeam
        ).Union(
          from fixture in fixtures
          where fixture.Played  && (fixture.HomeScore == fixture.AwayScore)
          select fixture.AwayTeam );
    

    根据固定装置的来源/实现方式,可能值得考虑“缓存”绘制的固定装置以避免必须枚举固定装置两次。

    var draws = 
        ( from fixture in fixtures
          where fixture.Played  && (fixture.HomeScore == fixture.AwayScore)
          select fixture
        ).ToList();
    
    IEnumerable<Team> drew =
        (from draw in draws select draw.HomeTeam)
        .Union(from draw in draws select draw.AwayTeam);
    

    或者使用流畅的风格:

    var draws = 
        fixtures
        .Where(fxtr => fxtr.Played && (fxtr.HomeScore == fxtr.AwayScore))
        .ToList();
    
    IEnumerable<Team> drew =
        draws.Select(fixture => fixture.HomeTeam)
        .Union(draws.Select(fixture => fixture.AwayTeam));
    

    修改 Fixture 类

    可以考虑将“ParticipatingTeams”添加到 Fixture 类中以获得:

    IEnumerable<Team> drew =
        from fixture in fixtures
        where fixture.Played && (fixture.HomeScore == fixture.AwayScore)
        from team in fixture.ParticipatingTeams
        select team;
    

    但正如@MattDeKrey 指出的那样,这需要更改合同。

    代码示例

    代码示例可在Repl.it获取

    【讨论】:

    • 您的第一个查询+1 - 不需要更改合同,并且比主要答案更有效。
    【解决方案3】:

    我认为您正在寻找 Union 方法如下:

    IEnumerable<Team> drew = (from fixture in fixtures
                         where fixture.Played 
                            && (fixture.HomeScore == fixture.AwayScore)
                         select fixture.HomeTeam)
                         .Union(from fixture in fixtures
                         where fixture.Played 
                            && (fixture.HomeScore == fixture.AwayScore)
                         select fixture.AwayTeam);
    

    【讨论】:

    【解决方案4】:

    我自己尝试了一下,想出了与“取决于”相同的版本。

    使用查询理解语法:

    IEnumerable<Team> drew =
        from fixture in fixtures
        where fixture.Played && (fixture.HomeScore == fixture.AwayScore)
        from team in new[]{fixture.AwayTeam, fixture.HomeTeam}
        select team;
    

    将 lambda 与扩展方法一起使用:

    IEnumerable<Team> drew =
        fixtures.Where(f => f.Played && f.HomeScore == f.AwayScore)
        .SelectMany(f => new[]{f.HomeTeam, f.AwayTeam});
    

    编辑:我不知道一支球队是否可能在您的数据库中多次参加比赛和抽签,但如果可能的话,那么您可能想使用Distinct查询运算符:

    IEnumerable<Team> drew =
        (from fixture in fixtures
         where fixture.Played && (fixture.HomeScore == fixture.AwayScore)
         from team in new[]{fixture.AwayTeam, fixture.HomeTeam}
         select team).Distinct();
    

    或:

    IEnumerable<Team> drew =
        fixtures.Where(f => f.Played && f.HomeScore == f.AwayScore)
        .SelectMany(f => new[]{f.HomeTeam, f.AwayTeam})
        .Distinct();
    

    【讨论】:

    • 最后一个样本对我有用。还教我可以使用类型推断来声明数组。每天都是上学日。
    【解决方案5】:

    或者您可以定义一个类型来保存所有数据:

    IEnumerable<TeamCluster> drew = from fixture in fixtures
                             where fixture.Played && (fixture.HomeScore == fixture.AwayScore)
                             select new TeamCluster {
                                 Team1 = fixture.HomeTeam,
                                 Team2 = fixture.AwayTeam,
                                 Score1 = fixture.HomeScore,
                                 Score2 = fixture.AwayScore
                             };
    
    class TeamCluster {
        public Team Team1 { get; set; }
        public Team Team2 { get; set; }
        public int Score1 { get; set; }
        public int Score2 { get; set; }
    }
    

    【讨论】:

      【解决方案6】:

      编辑:对不起,误解了你原来的问题,所以重写了答案。

      你可以使用“SelectMany”操作符来做你想做的事:

      IEnumerable<Team> drew =
                 (from fixture in fixtures
                  where fixture.Played && (fixture.HomeScore == fixture.AwayScore)
                        select new List<Team>()
                                   { HomeTeam = fixture.HomeTeam,
                                     AwayTeam = fixture.AwayTeam
                                   }).SelectMany(team => team);
      

      这将返回平局的球队名单。

      【讨论】:

        【解决方案7】:

        我遇到了这个问题,找不到我想要的,所以我写了一个小的扩展方法来做我想要的。

        public static IEnumerable<R> MapCombine<M, R>(this IEnumerable<M> origList, params Func<M, R>[] maps)
        {
            foreach (var item in origList)
            foreach (var map in maps)
            {
                yield return map(item);
            }
        }
        

        根据问题中的问题,您将执行类似的操作

        var drew = fixtures.Where(fixture => fixture.Played && 
                                (fixture.HomeScore == fixture.AwayScore))
                            .MapCombine(f => f.HomeTeam, f => f.AwayTeam);
        

        有趣的是,intellisense 对此并不完全满意,您不会在下拉列表的顶部看到 lamdba 表达式,但是在“=>”之后它非常满意。但最主要的是编译器很高兴。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-11-15
          • 1970-01-01
          • 1970-01-01
          • 2013-03-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多