C# несколько переменных в лямбда-выражении внутри запроса LinQ
Я работаю с календарем медсестры, который состоит из смен:
public interface IShift
{
ShiftType ShiftType { get; } //enum {Day, Early, Late, Night}
DateTime Day { get; }
bool IsNightShift();
bool IsWeekendShift();
}
календарь медсестры IEnumerable<IShift>
. Из этого я выбираю свои собственные смены (IEnumerable<IShift> selectedShifts
) в течение более короткого периода времени для проверки некоторых ограничений.
Я пытаюсь подсчитать несколько условий, например, ночные смены в пятницу:
var countFridayNight = selectedShifts.Count(s => s.IsNightShift()
&& s.Day.DayOfWeek == DayOfWeek.Friday);
то, с чем я борюсь, - это считать несколько вещей в несколько дней. Например, поздняя смена в пятницу и ранняя смена в следующую Понедельник. Я пробовал это, но VS, похоже, не нравится синтаксис:
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: удалены фигурные скобки в последнем лямбда-выражении.
Edit2: было два комментария о том, что Count не может принимать более одной переменной внутри лямбда-выражения. Как еще я могу сделать то, что мне нужно, используя LINQ?
Edit3: разъяснение проблемы - мне нужно подсчитать смены, которые являются поздними сменами в пятницу, и в то же время существует еще один сдвиг, который Рано утром в следующий понедельник.
6 ответов
вам нужно перекрестно присоединить коллекцию к себе для этой проблемы-по существу вам нужно получить каждый пара комбинаций сдвига и подсчета пар где первый-поздняя пятница, а второй-ранний понедельник.
сначала получите пары:
var pairs = selectedShifts.SelectMany(s => selectedShifts, Tuple.Create);
во-вторых, подсчитайте пары, которые соответствуют вашим критериям:
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);
вы можете получить пары более эффективно, если сдвиги уже упорядочены последовательно, и вы только хотите подсчитать смежные смены:
var pairs = selectedShifts.Zip(selectedShifts.Skip(1), Tuple.Create);
Если вы уменьшите входы до lateFridays и earlyMondays перед сопряжением, он должен идти немного быстрее.
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();
кроме того, это сравнение дат плохо для случаев, охватывающих год.
строго говоря, вы можете использовать что-то вроде:
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)
);
но с точки зрения производительности было бы лучше определить следующий сдвиг, связав их вместе или с подсписком. Например:
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);
последний предполагает, что следующая смена-это смена понедельника, а не какая-то смена выходных. С помощью этого последнего метода вы также можете проверить, меньше ли количество дней (или часов) между поздней сменой и следующей сменой, чем сумма "x"
Если я правильно прочитал вопрос, вы просто хотите OR
синтаксис. Это даст поздние смены в пятницу и рано в понедельник отдельно:
var countFridayLateAndMondayEarly =
selectedShifts.Count(
shift => (shift.ShiftType == ShiftType.Late && shift.Day.DayOfWeek == DayOfWeek.Friday)
|| // or
(shift.Day.DayOfWeek == DayOfWeek.Monday && shift.ShiftType == ShiftType.Early
));
Если вам нравится синтаксис true Linq, вы можете сделать это так.
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();
другой вариант Он не будет работать лучше, чем перекрестное соединение, так как он делает то же самое, но, возможно, немного более читаемый. Используйте его только в списках в памяти.
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))