Monday, March 28, 2011

Can I select multiple objects in a Linq query

I'm very new to Linq so bare with me. Can I return more than one item in a select? For instance I have a List of Fixtures (think football (or soccer for the yanks) fixtures). Each fixture contains a home and away team and a home and away score. I want to get all the teams that drew. I want to use something like

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

I know this syntax is incorrect, what I don't know is if it's possible to do this. Would I need two queries and then concatenate them?

Edit: this is really a learning thing so it's not critical to achieve this in any particular way. Basically, at this stage all i want is a list of teams that have drawn. An example usage might be that for a given list of fixtures i can find all of the drawn teams so that i could update their standings in a table by 1 point (3 for a win, 0 for a loss).

Cheers James

From stackoverflow
  • 101 LINQ Samples, namely Select - Anonymous Types 1

    ... select new { HomeTeam = fixture.HomeTeam, AwayTeam = fixture.AwayTeam };
    
    Mike Powell : Not the answer he's looking for. He wants a list of Teams, not a list of anonymous types with hometeam and awayteam properties.
    James Hay : This is true... i could get round it using anonymous types... just wondered if there was a way to get just a list of teams. If it's the only way it's the only way though
    bendewey : I agree that this doesn't return a list of teams, but i thinks its better for him to adapt his code to suport handling this anon type. If James Hay could update his question to describe his usuage that might help.
    Mike Powell : I think his question already describes his requirement perfectly: "I want to get a list of teams that drew." There are lots of reasons he might not want to use anonymous types here (needing to pass the list outside this method would be a common one).
  • Edit: Sorry, misunderstood your original question, so rewrote answer.

    You could use the "SelectMany" operator to do what you want:

    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);
    

    This will return a flattened list of teams that drew.

  • Or you can define a type to hold all that data:

    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; }
    }
    
  • I think you're looking for the Union method as follows:

    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);
    
  • An (independant) variation on John Price's solution...

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

    You could consider adding "ParticipatingTeams" to the Fixture class to get:

    IEnumerable<Team> drew =
        from fixture in fixtures
        where fixture.Played && (fixture.HomeScore == fixture.AwayScore)
        from team in fixture.ParticipatingTeams
        select team;
    
    mattdekrey : +1 for your first query - doesn't require a contract change and is more efficient than the leading answer.
  • Taking a stab at this myself I came up with the same version as 'it depends'.

    Using query comprehension syntax:

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

    Using lambda with extension methods:

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

    Edit: I don't know if a team could have possibly played and drawn more than once in your database, but if that's possible, then you might want to make use of the Distinct query operator:

    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();
    

    or:

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

0 comments:

Post a Comment