Royi Namir
Royi Namir

Reputation: 148624

Parent and Child fluent Linq query in C#?

I've encountered a problem in fluent LINQ for selecting a parent and a child into one Ienumerable < anonymous type>.

For simplicity :

I want to list each commander ( who has one or more solider who can win the war) and one tool name which is used by that soldier.

I dont care if a commander has more then one soldier who can win the war .

All a commander needs is a tool (never-mind which tool) from one of its soldiers (who can win the war).

the output is Ienumerable < anonymous type> where anonymous type is like :

{ 
  theCommander = [commanderObject] , 
  theToolName = [the tool name ]
} 

I did something with selectMany but it was very long and ugly.

Is there any short query (fluent query) which can yield the same results in a shorter way?

public class Commander
    {
    public List<Soldier> lstSoldiers =new List<Soldier>(); //from db actually.
    }


    public class Soldier
    {
      public bool IsCanWinWar { get; set; }
      public string Tool { get; set; }
    }



void Main()
{
  List<Commander> lstCommanders = new List<Commander>(); //data source is actually from db.
  var MyIernumerableOfAnonymouseTypes= ?
}

Upvotes: 2

Views: 1411

Answers (2)

Ryszard Dżegan
Ryszard Dżegan

Reputation: 25434

First approach (revised later):

from commander in lstCommanders
where commander.lstSoldiers.Any(soldier => soldier.IsCanWinWar) // Consider only commanders who have at least one soldier who can win the war
select new
            {
                Commander = commander,
                Tool = (from soldier in commander.lstSoldiers
                        where soldier.IsCanWinWar // Consider tool originating from soldiers who can win the war
                        select soldier.Tool).FirstOrDefault()
            };

Second more concise and faster:

from commander in lstCommanders
where commander.lstSoldiers.Any(soldier => soldier.IsCanWinWar)
select new { Commander = commander, Tool = commander.lstSoldiers.First(soldier => soldier.IsCanWinWar).Tool };

In the fluent way:

lstCommanders.Where(commander => commander.lstSoldiers.Any(soldier => soldier.IsCanWinWar))
             .Select(  commander => new
                                         {
                                                 Commander = commander,
                                                 Tool = commander.lstSoldiers.First(soldier => soldier.IsCanWinWar).Tool
                                         });

Third option with one iteration:

from commander in lstCommanders
let winner = commander.lstSoldiers.FirstOrDefault(soldier => soldier.IsCanWinWar)
where null != winner // Consider only commanders who have at least one soldier who can win the war
select new { Commander = commander, Tool = winner.Tool };

Upvotes: 1

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236268

var MyIernumerableOfAnonymouseTypes = 
   lstCommanders.Select(c => new { 
                    theCommander = c, 
                    theToolName = c.lstSoldiers.Where(s => s.IsCanWinWar)
                                               .Select(s => s.Tool)
                                               .FirstOrDefault() })
                .Where(x => x.theToolName != null);

If there is no soldier which can win war, then tool will be null. Just filter out those commanders after selection.

BTW I think Soldiers is a better property name, then lstSoldiers (it has no prefix and it is PascalCase).

Upvotes: 2

Related Questions