How to remove first element from linq query - c#

Im trying to consolidate a list of records of in and out times per day to the minimum number of records possible.
What i have done so far, is grouped up the lines into the groups they need to be in, and put the in and out times in a list for each day.
then i want to process the lists and add the first set of in and out lines onto a single line, then process the next entry and either create a new line or fill in the blanks of the previous line.
The bit im stuck with is removing the first item from the linq result after i have processed it.
happy to look at doing it a different way.
here is what i have:
List<LoginRecordLine> condensedLoginRecordLines = new List<LoginRecordLine>();
List<LoginRecordLine> currentLoginRecordLines = GetLoginRecordsForLoginRecordReport(lowerDate, upperDate, sageDatabaseID, loggedInUserID);
var groupedLines = from LoginRecordLine line in currentLoginRecordLines
group line by new { line.TimesheetID, line.WorkPatternPayRateID } into g
select new
{
Lines = g,
TimesheetID = g.Key.TimesheetID,
PayRateID = g.Key.WorkPatternPayRateID
};
foreach (var g in groupedLines)
{
var monTimes = from line in g.Lines
orderby line.MonTimeIn ascending
where line.MonTimeSpan != TimeSpan.Zero
select new
{
TimeIn = line.MonTimeIn,
TimeOut = line.MonTimeOut,
Timesheet = line.Timesheet,
PayRate = line.WorkPatternPayRate
};
var tueTimes = //Same as monday
var wedTimes = //Same as monday
var thuTimes = //same as monday
var friTimes = //same as monday
var satTimes = //same as monday
var sunTimes = //same as monday
while (monTimes.Count() != 0 || tueTimes.Count() != 0 || wedTimes.Count() != 0 || thuTimes.Count() != 0 || friTimes.Count() != 0 || satTimes.Count() != 0 || sunTimes.Count() != 0)
{
LoginRecordLine condensedLine = new LoginRecordLine();
if (monTimes.Count() >0)
{
condensedLine.MonTimeIn = monTimes.First().TimeIn;
condensedLine.MonTimeOut = monTimes.First().TimeOut;
condensedLine.Timesheet = monTimes.First().Timesheet;
condensedLine.WorkPatternPayRate = monTimes.First().PayRate;
//*************** REVELANT PART *************/
//remove first item from monday list
}
// tue
// wed
// etc
}
}
return condensedLoginRecordLines;
Update - Working code - before performance changes
List<LoginRecordLine> condensedLoginRecordLines = new List<LoginRecordLine>();
List<LoginRecordLine> currentLoginRecordLines = GetLoginRecordsForLoginRecordReport(lowerDate, upperDate, sageDatabaseID, loggedInUserID);
var groupedLines = from LoginRecordLine line in currentLoginRecordLines
group line by new { line.TimesheetID, line.WorkPatternPayRateID } into g
select new
{
Lines = g,
TimesheetID = g.Key.TimesheetID,
PayRateID = g.Key.WorkPatternPayRateID
};
foreach (var g in groupedLines)
{
var monTimes = (from line in g.Lines
orderby line.MonTimeIn ascending
where line.MonTimeSpan != TimeSpan.Zero
select new
{
TimeIn = line.MonTimeIn,
TimeOut = line.MonTimeOut,
Timesheet = line.Timesheet,
PayRate = line.WorkPatternPayRate
}).ToList();
var tueTimes = //Same as monday
var wedTimes = //Same as monday
var thuTimes = //same as monday
var friTimes = //same as monday
var satTimes = //same as monday
var sunTimes = //same as monday
while (monTimes.Count != 0 || tueTimes.Count != 0 || wedTimes.Count != 0 || thuTimes.Count != 0 || friTimes.Count != 0 || satTimes.Count != 0 || sunTimes.Count != 0)
{
LoginRecordLine condensedLine = new LoginRecordLine();
if (monTimes.Count >0)
{
condensedLine.MonTimeIn = monTimes.First().TimeIn;
condensedLine.MonTimeOut = monTimes.First().TimeOut;
condensedLine.Timesheet = monTimes.First().Timesheet;
condensedLine.WorkPatternPayRate = monTimes.First().PayRate;
condensedLoginRecordLines.Add(condensedLine);
monTimes.RemoveAt(0);
}
//etc
}
}
return condensedLoginRecordLines;

use the List.RemoveAt Method something like myList.RemoveAt(0) will remove the first item of your list

You should revise your algorithm and maybe data structures.
For anonymous types in queries I would add a DayOfWeek property, so the queries will look like:
var monTimes = from line in g.Lines
orderby line.MonTimeIn ascending
where line.MonTimeSpan != TimeSpan.Zero
select new
{
TimeIn = line.MonTimeIn,
TimeOut = line.MonTimeOut,
Timesheet = line.Timesheet,
PayRate = line.WorkPatternPayRate,
WeekDay = DayOfWeek.Monday
};
Then, the final loop will be replaced by something like:
var condensedLoginRecordLines = monTimes
.Concat(tueTimes)
.Concat(wedTimes)
..//etc
.Select(data => new CondensedLine { WeekDay = data.WeekDay, /* here all the properties are initialized */ })
.ToList();
And that's all.
If you still prefer to use the MonInTime, TueInTime, etc. properties, move the creation of CondensedLine into a separate function which applies a switch on the WeekDay and initializes the relevant properties only. In this case you should declare a private class instead the anonymous types you currently use in order to pass the information from method to another.

I came across a similar problem. In my case, the source data was a CSV file parsed into a string and I wanted to remove the header information before processing. I could have used the approach of casting to a list and removing the first entry, but I discovered that it is possible to add a second parameter to the where query that would give you the row number index. This works with both IQueryable and IEnumerable overloads as well.
public static List<string> GetDataLinesFromCSV(string csv)
{
var csvLines = csv.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);
var dataLines = csvLines.AsQueryable().Where((x, idx) => idx > 0).ToList();
return dataLines;
}

Related

Get the highest date in a List of object and get another property of its class [duplicate]

This question already has answers here:
How to use LINQ to select object with minimum or maximum property value
(20 answers)
Closed 2 years ago.
I have a class that is like this:
class DateTimeProperty
{
public string Id { get; set; }
public DateTime TDateTime { get; set; }
}
and I use it like so:
List<DateTimeProperty> dtp = new List<DateTimeProperty>();
and I fill the list like so:
var item = new DateTimeProperty
{
Id = id,
TDateTime = transactioDateTime
};
dtp.Add(item);
Then I get the maximum date like so
maxDate = dtp.Max(x => x.TDateTime);
but what I need is the Id that has that date. Can you please show me how to get this? The whole code is below:
static void Main(string[] args)
{
List<DateTimeProperty> dtp = new List<DateTimeProperty>();
var format = "dd/MM/yyyy HH:mm:ss.fff";
var folderPath = #"I:\LCS Transactions";
DirectoryInfo di = new DirectoryInfo(folderPath);
List<DateTime> dateTimeArray = new List<DateTime>();
DateTime maxDate;
FileInfo[] fi = di.GetFiles();
foreach(FileInfo f in fi)
{
var file = GetHeader(f.FullName);
if (!file.Contains(".txt"))
{
if (file.Contains("_"))
{
var spike = file.Split('_');
var transactionType = spike[0];
var dateTime = spike[3];
var id = spike[4];
var year = dateTime.Substring(0, 4);
var month = dateTime.Substring(4, 2);
var day = dateTime.Substring(6,2);
var hour = dateTime.Substring(8, 2);
var minute = dateTime.Substring(10, 2);
var seconds = dateTime.Substring(12, 2);
var miiliseconds = dateTime.Substring(14, 3);
var stringDate = $#"{day}/{month}/{year} {hour}:{minute}:{seconds}.{miiliseconds}";
DateTime transactioDateTime = DateTime.ParseExact(stringDate, format, CultureInfo.InvariantCulture);
var item = new DateTimeProperty
{
Id = id,
TDateTime = transactioDateTime
};
Console.WriteLine(transactioDateTime);
//dateTimeArray.Add(transactioDateTime);
dtp.Add(item);
}
}
}
maxDate = dtp.Max(x => x.TDateTime);
Console.WriteLine("=====================");
Console.WriteLine(maxDate);
Console.ReadLine();
}
You can just use OrderByDescending, then FirstOrDefault
var result = dtp.OrderByDescending(x => TDateTime).FirstOrDefault();
if(result != null)
Console.WriteLine(result.Id);
Additional Resources
Enumerable.OrderByDescending Method
Sorts the elements of a sequence in descending order.
Enumerable.FirstOrDefault Method
Returns the first element of a sequence, or a default value if no
element is found.
Just order by the date and take the Id property from the first item:
var maxDateId = dtp
.OrderByDescending(x => x.TDateTime)
.FirstOrDefault()?.Id;
FirstOrDefault can return null if the sequence is empty, which is why I use the null conditional operator (?.) to access the Id property. If the sequence is empty, maxDateId will be null, so you might have to check for this to make your code safe from a NullReferenceException.
You can avoid paying the performance price of sorting the list by using the LINQ Aggregate method.
var maxDateItem = dtp.Aggregate((DateTimeProperty)null, (max, item) =>
{
return max == null || item.TDateTime > max.TDateTime ? item : max;
});
var maxDateItemId = maxDateItem.Id; // Will throw if the list has no elements
Update. More solutions here: How to use LINQ to select object with minimum or maximum property value.

Make group from list string

I have one List as below:
var paths = new List<string> {
#"rootuploaded\samplefolder\1232_234234_1.jpg",
#"rootuploaded\samplefolder\1232_2342.jpg",
#"rootuploaded\samplefolder\subfolder\1232_234234_1.jpg",
#"rootuploaded\samplefolder\subfolder\1232_2342.jpg",
#"rootuploaded\file-­5.txt",
#"rootuploaded\file­-67.txt",
#"rootuploaded\file­-a.txt",
#"rootuploaded\file1.txt",
#"rootuploaded\file5.txt",
#"rootuploaded\filea.txt",
#"rootuploaded\text.txt",
#"rootuploaded\file_sample_a.txt",
#"rootuploaded\file2.txt",
#"rootuploaded\file_sample.txt",
#"rootuploaded\samplefolder\1232_234234_2.bmp",
};
How to print output like this:
○ Group 1
rootuploaded\samplefolder\1232_234234_1.jpg,
rootuploaded\samplefolder\1232_234234_2.bmp
○ Group 2
rootuploaded\file1.txt
rootuploaded\file2.txt
rootuploaded\file5.txt
○ Group 3
rootuploaded\file-5.txt
rootuploaded\file-67.txt
○ Group 4
rootuploaded\file_sample.txt
rootuploaded\file_sample_a.txt
○ Cannot grouped
rootuploaded\samplefolder\1232_2342.jpg
rootuploaded\file-a.txt
rootuploaded\filea.txt
rootuploaded\text.txt
Grouping files based on 6 naming conventions (with top¬down priority):
FileName.ext, FileName_anything.ext, FileName_anythingelse.ext, ...
FileName.ext, FileName-anything.ext, FileName-anythingelse.ext, ...
FileName_1.ext, FileName_2.ext, ..., FileName_N.ext (maybe not continuous)
FileName-1.ext, FileName-2.ext, ..., FileName-N.ext (maybe not continuous)
FileName 1.ext, FileName 2.ext, ..., FileName N.ext (maybe not continuous)
FileName1.ext, FileName2.ext, ..., FileNameN.ext (maybe not continuous)
I used Linq to separate:
var groups1 = paths.GroupBy(GetFileName, (key, g) => new
{
key = key,
count = g.Count(),
path = g.ToList()
}).Where(x => x.count < 5 && x.count >= 2).ToList();
public string GetFileName(string fileName)
{
var index = 0;
if (fileName.Contains("_"))
index = fileName.IndexOf("_", StringComparison.Ordinal);
else if (fileName.Contains("-"))
index = fileName.IndexOf("-", StringComparison.Ordinal);
var result = fileName.Substring(0, index);
return result;
}
Try doing this:
var groups = new []
{
new { regex = #"rootuploaded\\samplefolder\\1232_234234_\d\..{3}", grp = 1 },
new { regex = #"rootuploaded\\file\d\.txt", grp = 2 },
new { regex = #"rootuploaded\\file-\d+\.txt", grp = 3 },
new { regex = #"rootuploaded\\file_sample.*\.txt", grp = 4 },
};
var results =
from path in paths
group path by
groups
.Where(x => Regex.IsMatch(path, x.regex))
.Select(x => x.grp)
.DefaultIfEmpty(99)
.First()
into gpaths
orderby gpaths.Key
select new
{
Group = gpaths.Key,
Files = gpaths.ToArray(),
};
That gives you this:
You would just have to jig around with the regex until you get exactly what you want.
Sadly, 1. and 2. group turn this solution difficult. Cause both contain 'FileName.ext', so it has to check whole list together :(
I try to separate groupping 1. 2. and 3 - 6:
First step:
Find and remove Group 1 and 2 candidates.
It orders the list base on file path:
var orderedFilenames = pathsDistinct().OrderBy(p => p).ToList();
Than find Group 1 and 2 candidates:
var groupped = orderedFilenames.GroupBy(s => GetStarterFileName(s, orderedFilenames));
private static string GetStarterFileName(string fileNameMatcher, List<string> orderedFilenames)
{
string fileNameMatcherWOExt = Path.GetFileNameWithoutExtension(fileNameMatcher);
return orderedFilenames.FirstOrDefault(p =>
{
if (p == fileNameMatcher) return true;
string p_directory = Path.GetDirectoryName(p);
string directory = Path.GetDirectoryName(fileNameMatcher);
if (p_directory != directory) return false;
string pure = Path.GetFileNameWithoutExtension(p);
if (!fileNameMatcherWOExt.StartsWith(pure)) return false;
if (fileNameMatcherWOExt.Length <= pure.Length) return false;
char separator = fileNameMatcherWOExt[pure.Length];
if (separator != '_' && separator != '-') return false;
return true;
});
}
Step two:
After first step, you got Group 1 and 2 candidates, but all others are separated into different groups.
Collect remaining path and separete group 1 and 2:
var mergedGroupps = groupped.Where(grp => grp.Count() == 1).SelectMany(grp => grp);
var starterFileNameGroups = groupped.Where(grp => grp.Count() > 1);
Step three
Now you could find 3-6 based on regex validation:
var endWithNumbersGroups = mergedGroupps.GroupBy(s => GetEndWithNumber(s));
private static string GetEndWithNumber(string fileNameMatcher)
{
string fileNameWithoutExtesion = Path.Combine(Path.GetDirectoryName(fileNameMatcher), Path.GetFileNameWithoutExtension(fileNameMatcher));
string filename = null;
filename = CheckWithRegex(#"_(\d+)$", fileNameWithoutExtesion, 1);
if (filename != null) return filename;
filename = CheckWithRegex(#"-(\d+)$", fileNameWithoutExtesion, 1);
if (filename != null) return filename;
filename = CheckWithRegex(#" (\d+)$", fileNameWithoutExtesion, 1);
if (filename != null) return filename;
filename = CheckWithRegex(#"(\d+)$", fileNameWithoutExtesion);
if (filename != null) return filename;
return fileNameWithoutExtesion;
}
private static string CheckWithRegex(string p, string filename, int additionalCharLength = 0)
{
Regex regex = new Regex(p, RegexOptions.Compiled | RegexOptions.CultureInvariant);
Match match = regex.Match(filename);
if (match.Success)
return filename.Substring(0, filename.Length - (match.Groups[0].Length - additionalCharLength));
return null;
}
Final Step:
Collect non groupped items and merge Group 1-2 and 3-6 candidates
var nonGroupped = endWithNumbersGroups.Where(grp => grp.Count() == 1).SelectMany(grp => grp);
endWithNumbersGroups = endWithNumbersGroups.Where(grp => grp.Count() > 1);
var result = starterFileNameGroups.Concat(endWithNumbersGroups);
You could try to solve both step in one shot, but as you see groupping mechanism are different. My solution is not so beautiful, but I think it's clear... maybe :)

foreach to update values in object overwrites values

Caveat: I know some parts of this code are bad, but it is what it is for now. I just want it to run properly. I fully intend to refactor later. I just need a working app right now.
The initial linq query grabs several fields of data, but more data must be added per item in the resultset. So, we have the foreach below. It grabs data and updates each row.
It overwrites everything to what I'm thinking is probably the last iteration of the foreach. Why? How do I keep it from overwriting?
Keep in mind that the working variable just meabns a period id. I want to get previous or future periods, and subtracting or adding to this allows this.
public List<intranetGS.Forecast> getForecast(int branchId) {
//user role protection here
intraDataContext q = new intraDataContext();
//this grabs the initial data
var basefc = (from f in q.fc_items
where f.color_option == false
select new intranetGS.Forecast {
itemId = f.item_id,
item = f.item_number,
itemDesc = f.description,
itemSuffix = f.item_suffix,
itemPrefix = f.item_prefix,
designation = f.designation
});
//now we filter
switch (getDesignation(branchId)) {
case 1:
basefc = basefc.Where(n => n.designation != 3);
basefc = basefc.Where(n => n.designation != 6);
break;
case 2:
basefc = basefc.Where(n => n.designation > 3);
break;
case 3:
basefc = basefc.Where(n => n.designation != 2);
basefc = basefc.Where(n => n.designation != 6);
break;
}
var current = Convert.ToInt32(DateTime.Now.Month);
var working = 0;
var year = Convert.ToInt32(DateTime.Now.Year);
List<intranetGS.Forecast> res = new List<intranetGS.Forecast>();
foreach (var f in basefc) {
working = getPeriod(current + "/" + (year - 1)); //starting with last year;
var ly = (from l in q.fc_forecasts where l.period == working && l.branch == branchId && l.item == f.itemId select l).FirstOrDefault();
if (!object.ReferenceEquals(ly, null)) {
f.lastYearForecast = ly.forecast;
f.lastYearReceipt = ly.receipt;
}
working = getPeriod(current + "/" + year) - 2; //two months ago
var m2 = (from l in q.fc_forecasts where l.period == working && l.branch == branchId && l.item == f.itemId select l).FirstOrDefault();
if (!object.ReferenceEquals(m2, null)) {
f.twoMosForecast = m2.forecast;
f.twoMosReceipts = m2.receipt;
f.twoMosUsage = m2.usage_lb;
}
working = getPeriod(current + "/" + year) - 1; //one month ago
var m1 = (from l in q.fc_forecasts where l.period == working && l.branch == branchId && l.item == f.itemId select l).FirstOrDefault();
if (!object.ReferenceEquals(m1, null)) {
f.oneMosForecast = m1.forecast;
f.oneMosReceipts = m1.receipt;
f.oneMosUsage = m1.usage_lb;
}
working = getPeriod(current + "/" + year); //current month
var m = (from l in q.fc_forecasts where l.period == working && l.branch == branchId && l.item == f.itemId select l).FirstOrDefault();
if (!object.ReferenceEquals(m, null)) {
f.currentMosForecast = m.forecast;
f.currentMosReceipts = m.receipt;
f.currentMosusage = m.usage_lb;
}
working = getPeriod(current + "/" + year) + 1; //one month from now
var mnext1 = (from l in q.fc_forecasts where l.period == working && l.branch == branchId && l.item == f.itemId select l).FirstOrDefault();
if (!object.ReferenceEquals(mnext1, null)) {
f.plusOneForecast = mnext1.forecast;
f.plusOneForecastId = mnext1.forcast_id;
}
working = getPeriod(current + "/" + year) + 2; //two months from now
var mnext2 = (from l in q.fc_forecasts where l.period == working && l.branch == branchId && l.item == f.itemId select l).FirstOrDefault();
if (!object.ReferenceEquals(mnext2, null)) {
f.plusTwoForecast = mnext2.forecast;
f.plusTwoForecastId = mnext2.forcast_id;
}
} //this is insanely and extremely cumbersome; refactor later.
return basefc;
}
UPDATE: It wasn't a list, it needed to be a list to avoid the overwrite.
The issue is that there is a delayed execution which occurs in linq as the user is building the query and internally it is building an expression tree where new expressions can be added. Once the query factors are settled upon during an execution, such as an enumerable target in a for loop or via .ToList() that list is fluid still. Since the code was simply adding more expressions and not filtering it out into a new list, the query just grew.
The question is when working on existing code, did the developer want to keep building the expression tree for performance or did they intend to make the items concrete at each step along the process?.
You may be fixing an issue by making the initial list concrete but could be introducing a logic bug going forward. Keep that in mind.

LINQ get objects by date interval

I'm looking for a LINQ query that will select only those objects whose date interval is not higher than 20 seconds. For example:
AuthenticationEssay[] essays = new AuthenticationEssay[] {
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(20), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(24), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(29), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(38), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(125), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(347), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(400), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(422), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(446), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(467), Success = false }
};
I want to select only the first occurence of those objects whose date interval is not longer than 20 seconds against the next object. In this case, the query should return only the first 4 objects. Any idea? :(
UPDATE
Sorry, I forgot to mention that I'm sorting the array by descending order. So yes, the position in the array shouldn't have any effect on the query.
What about this?
var query from i in Enumerable.Range(1, count - 1)
let current = list[i]
let previous = list[i - 1]
// I see some empty positions in your example, nullability check
where current != null && previous != null
where (current.Date - previous.Date).TotalSeconds < 20
select previous;
EDIT: Obviously you have to call First() in order to get only the first element of the sequence.
query.First();
EDIT 2: I have just read you are ordering your results descending. In this case the query will be slightly different:
var query from i in Enumerable.Range(1, count - 1)
let current = list[i]
let previous = list[i - 1]
// I see some empty positions in your example, nullability check
where current != null && previous != null
where (previous.Date - current.Date).TotalSeconds < 20
select current;
It ain't pretty, but here you go...
var result = Enumerable.Range(0, essays.Count() - 1)
.Select(i => new {Essays1 = essays[i], Essays2 = essays[i + 1]})
.Where(a => a.Essays2 != null)
.Where(a => a.Essays2.Date - a.Essays1.Date < new TimeSpan(0, 0, 0, 20))
.Select(a => a.Essays1);
Does it have to be LINQ? I love LINQ, but I think something like this would be more readable...
var result = new List<AuthenticationEssay>();
for (var i = 0; i < (essays.Count() - 1); i++)
{
if (essays[i + 1] != null)
if (essays[i + 1].Date - essays[i].Date < new TimeSpan(0, 0, 0, 20))
result.Add(essays[i]);
}
It is probably possible to do it using built-in Linq operators, but in this case I think writing a specific method is easier. You could do something like that:
static IEnumerable<AuthenticationEssay> Filter(IEnumerable<AuthenticationEssay> list)
{
AuthenticationEssay last = null;
AuthenticationEssay previous = null;
foreach(var item in list)
{
if (last == null)
{
// Always return the first item
yield return item;
}
else if ((item.Date - last.Date).TotalSeconds >= 20)
{
yield return item;
}
previous = last;
last = item;
}
if (previous != null && last != null && (last.Date - previous.Date).TotalSeconds <= 20)
yield return last;
}
Of course it would be possible to make it more reusable, by making the method generic and passing a predicate as a parameter, but since it's a very specific requirement, I'm not sure it would be very useful...

Combining 2 LINQ into one call

I'm using 2 similar LINQ queries to return a result, the only difference is the where clause (&& s.OptIn == "Yes"). Is there a way to execute this with only one query?
Instead of having a result of
A 2
B 3
and another result of
A 1
B 1
I want to have
A 2 1
B 3 1
Here's the LINQ:
var result = from s in pdc.ScanLogs
from e in pdc.Exhibits
from ce in pdc.ClientEvents
where s.ExhibitID == e.ExhibitID
&& e.ClientEventID == ce.ClientEventID
group 1 by new { ce.EventID } into d
select new {
EventID = d.Key.EventID,
Count = d.Count()
};
var result = from s in pdc.ScanLogs
from e in pdc.Exhibits
from ce in pdc.ClientEvents
where s.ExhibitID == e.ExhibitID
&& e.ClientEventID == ce.ClientEventID
&& s.OptIn == "Yes"
group 1 by new { ce.EventID } into d
select new {
EventID = d.Key.EventID,
Count = d.Count()
};
You can supply a predicate in the Count method. An example is below:
List<int> list = new List<int> { 1, 2, 3, 4, 5 };
var counts = new { CountAll = list.Count(), CountEven = list.Count(i => i % 2 == 0) };
Console.WriteLine(counts.CountEven);
A similar query written for Linq-To-Entities also worked and produced working SQL.
I haven't fully reconstructed your sample, but you should be able to rework it to something like this.
var result = from s in pdc.ScanLogs
from e in pdc.Exhibits
from ce in pdc.ClientEvents
where s.ExhibitID == e.ExhibitID
&& e.ClientEventID == ce.ClientEventID
group new { s, e, ce } by new { ce.EventID } into d
select new
{
EventID = d.Key.EventID,
Count = d.Count(),
CountOptIn = d.Count(item => item.s.OptIn == "Yes")
};
IQueryable<ScanLog> scanlogs = pdc.ScanLogs;
if (filter) scanlogs = scanlogs.Where(...);
var result = from s in scanlogs
...

Categories

Resources