Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I select multiple objects in a Linq query

Tags:

c#

list

linq

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

like image 238
James Hay Avatar asked Feb 06 '09 15:02

James Hay


People also ask

Can LINQ query work with array?

Yes it supports General Arrays, Generic Lists, XML, Databases and even flat files. The beauty of LINQ is uniformity.

Is LINQ better than SQL?

More importantly: when it comes to querying databases, LINQ is in most cases a significantly more productive querying language than SQL. Compared to SQL, LINQ is simpler, tidier, and higher-level.

How many ways can you write the LINQ query?

LINQ provides you three different ways to write a LINQ query in C# or VB.


2 Answers

101 LINQ Samples, namely Select - Anonymous Types 1

... select new { HomeTeam = fixture.HomeTeam, AwayTeam = fixture.AwayTeam }; 
like image 79
Anton Gogolev Avatar answered Oct 03 '22 22:10

Anton Gogolev


The following will return an IEnumerable<Team>:

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

Or, with the fluent style of LINQ:

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

Flattening and FlatMap

This requirement is often called 'flattening'. That is, taking a <Collection of <Collections of Things>> and converting it to a <Collection of Things>.

SelectMany both maps (a fixture to an Array of Teams) and flattens (a sequence of Team Arrays to a sequence of Teams). It is similar to the "flatMap" function in other languages such as Java and JavaScript.

It is possible to separate the Mapping and the 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); 

Other Approaches

Iterator Block

The same can be achieved with an iterator block, but I suspect this is rarely the best approach:

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

Union

Union is also an option but has the potential to produce different results from the above:

  1. The order of results will be different. All Home results are returned then all Away results.

  2. Union enumerates fixtures twice, so, depending on how fixtures is implemented, there is the potential for fixtures to be updated between calls. E.g., if a new drawn fixture were added between calls then the Away team could be returned but not the Home team.

As Mike Powell describes:

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

Depending on how fixtures is sourced/implemented it may be worth considering 'caching' the drawn fixtures to avoid having to enumerate fixtures twice.

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

Or using the fluent style:

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

Modifying the Fixture class

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

but as @MattDeKrey points out that requires a contract change.

Code Samples

Code samples are available on Repl.it

like image 21
codybartfast Avatar answered Oct 03 '22 20:10

codybartfast