C # multiple variables in lambda expression inside LinQ query

I work with a nurse calendar, which consists of Shifts:

public interface IShift { ShiftType ShiftType { get; } //enum {Day, Early, Late, Night} DateTime Day { get; } bool IsNightShift(); bool IsWeekendShift(); } 

Nursing Calendar IEnumerable<IShift> . From this, I select my own shifts ( IEnumerable<IShift> selectedShifts ) for a shorter period of time to check for some limitations.

I am trying to calculate several conditions, for example, Night Shifts on Friday:

 var countFridayNight = selectedShifts.Count(s => s.IsNightShift() && s.Day.DayOfWeek == DayOfWeek.Friday); 

What I'm struggling with is counting a few things a few days. For example, late shift on Friday and early shift next Monday. I tried this, but VS doesn't look like syntax:

 var countFridayLateAndMondayEarly = selectedShifts.Count( (r, s) => s.ShiftType == ShiftType.Late && s.Day.DayOfWeek == DayOfWeek.Friday && r.Day.DayOfWeek == DayOfWeek.Monday && r.Day.AddDays(-2).DayOfYear == s.Day.DayOfYear && r.ShiftType == ShiftType.Early ); 

Edit: Removed curly braces in the last lambda expression.

Edit2: There were two comments saying that a graph cannot accept more than one variable inside a lambda expression. How else can I do what I need to use LINQ?

Edit3: Clarification of the problem - I need to count the shifts, which are late shifts on Friday, and at the same time there is another shift, which is at the beginning of next Monday.

+8
c # lambda linq
source share
6 answers

You need to cross-attach the collection to yourself for this problem - essentially you need to get each pair of shift combinations and count the pairs, where the first is at the end of Friday and the second at the beginning of Monday.

First get the pairs:

 var pairs = selectedShifts.SelectMany(s => selectedShifts, Tuple.Create); 

Secondly, count pairs matching your criteria:

 var count = pairs.Count(pair => pair.Item1.ShiftType == ShiftType.Late && pair.Item1.Day.DayOfWeek == DayOfWeek.Friday && pair.Item2.Day.DayOfWeek == DayOfWeek.Monday && pair.Item2.Day.AddDays(-2).DayOfYear == pair.Item1.Day.DayOfYear && pair.Item2.ShiftType == ShiftType.Early); 

You can get pairs more efficiently if the shifts are already ordered sequentially, and you only want to count adjacent shifts:

 var pairs = selectedShifts.Zip(selectedShifts.Skip(1), Tuple.Create); 
+6
source share

If you reduce the input to lateFridays and earlyMondays before pairing, it should go a little faster.

 var lateFridays = selectedShifts .Where(s => s.ShiftType == ShiftType.Late && s.Day.DayOfWeek == DayOfWeek.Friday) .ToList(); var earlyMondays = selectedShifts .Where(r => r.Day.DayOfWeek == DayOfWeek.Monday && r.ShiftType == ShiftType.Early) .ToList(); var matchingPairs = lateFridays.SelectMany(friday => earlyMondays, Tuple.Create) .Where(t => t.Item2.Day.AddDays(-2).DayOfYear == t.Item1.Day.DayOfYear); var count = matchingPairs.Count(); 

In addition, this date comparison is bad for cross-border year cases.

+3
source share

Strictly speaking, you can use something like:

  var cnt = selectedShifts.Count(shift => shift.Day.DayOfWeek == DayOfWeek.Friday && shift.ShiftType==ShiftType.Late && selectedShifts.Any(next =>next.Day == shift.Day.AddDays(3) && next.ShiftType == ShiftType.Early) ); 

But in terms of performance, it would be better to define the next shift by connecting them together or with an additional list. For example:

  var lst= selectedShifts.ToList(); //Assuming already ordered, otherwise add an 'OrderBy' before the 'ToList' var cnt = lst.Where((shift,index)=> shift.Day.DayOfWeek == DayOfWeek.Friday && shift.ShiftType==ShiftType.Late && index < lst.Count-1 && lst[index+1].Day == shift.Day.AddDays(3) && lst[index+1].ShiftType == ShiftType.Early); 

The latter assumes that the next shift is a shift on Monday, and not a switch to the weekend. Using this last method, you can also check if the number of days (or hours) between the late shift and the next shift is less than the sum of "x"

+1
source share

If I read the question correctly, you just need the OR syntax. This will give late shifts on Friday and early Monday separately:

 var countFridayLateAndMondayEarly = selectedShifts.Count( shift => (shift.ShiftType == ShiftType.Late && shift.Day.DayOfWeek == DayOfWeek.Friday) || // or (shift.Day.DayOfWeek == DayOfWeek.Monday && shift.ShiftType == ShiftType.Early )); 
0
source share

If you like the real Linq syntax, you can do it as follows.

  var matchingPairs = from lateFriday in (from r in selectedShifts where r.Day.DayOfWeek == DayOfWeek.Monday && r.ShiftType == ShiftType.Early select r) from earlyMonday in (from s in selectedShifts where s.Day.DayOfWeek == DayOfWeek.Monday && s.ShiftType == ShiftType.Early select s) where earlyMonday.Day.AddDays(-2).DayOfYear == lateFriday.Day.DayOfYear select new { lateFriday, earlyMonday }; var count = matchingPairs.Count(); 
0
source share

Another option. It will not be better than a cross join, as it does the same, but perhaps a little readable. Use it only in lists in memory.

 selectedShifts.Where(x => x.Day.DayOfWeek == DayOfWeek.Friday && x.ShiftType == ShiftType.Late && list.Any(y => x.Day.AddDays(3) == y.Day && y.ShiftType == ShiftType.Early)) 
0
source share

All Articles