简体   繁体   中英

Linq query to group by field1, count field2 and filter by count between values of joined collection

I'm having trouble with getting a my linq query correct. I've been resisting doing this with foreach loops because I'm trying to better understand linq.

I have following data in LinqPad.

void Main()
{
    var events = new[] {
        new {ID = 1, EventLevel = 1, PatientID = "1", CodeID = "2", Occurences = 0 },
        new {ID = 2, EventLevel = 2, PatientID = "1", CodeID = "2", Occurences = 0 },
        new {ID = 3, EventLevel = 1, PatientID = "2", CodeID = "1", Occurences = 0 },
        new {ID = 4, EventLevel = 3, PatientID = "2", CodeID = "2", Occurences = 0 },
        new {ID = 5, EventLevel = 1, PatientID = "3", CodeID = "3", Occurences = 0 },
        new {ID = 6, EventLevel = 3, PatientID = "1", CodeID = "4", Occurences = 0 }
    };

    var filter = new FilterCriterion();
    var searches = new List<FilterCriterion.Occurence>();
    searches.Add(new FilterCriterion.Occurence() { CodeID = "1", MinOccurences = 2, MaxOccurences = 3 });
    searches.Add(new FilterCriterion.Occurence() { CodeID = "2", MinOccurences = 2, MaxOccurences = 3 });

    filter.Searches = searches;

    var summary = from e in events
        let de = new
        {
            PatientID = e.PatientID,
            CodeID = e.CodeID
        }
        group e by de into t
        select new
        {
            PatientID = t.Key.PatientID,
                CodeID = t.Key.CodeID,
            Occurences = t.Count(d => t.Key.CodeID == d.CodeID)
        };

    var allCodes = filter.Searches.Select(i => i.CodeID);

    summary = summary.Where(e => allCodes.Contains(e.CodeID));

    // How do I find the original ID property from the "events" collection and how do I 
    // eliminate the instances where the Occurences is not between MinOccurences and MaxOccurences.

    foreach (var item in summary)
        Console.WriteLine(item);
}

public class FilterCriterion
{

   public IEnumerable<Occurence> Searches { get; set; }

   public class Occurence
   {
       public string CodeID { get; set; }
       public int? MinOccurences { get; set; }
       public int? MaxOccurences { get; set; }
   }

}

The problem I have is that need to filter the results by the MinOccurences and MaxOccurences filter property and in the end I want the "events" objects where the IDs are 1,2,3 and 4.

Thanks in advance if you can provide help.

To access event.ID at the end of processing you need to pass it with your first query. Alter select to this:

// ...
group e by de into t
select new
{
    PatientID = t.Key.PatientID,
    CodeID = t.Key.CodeID,
    Occurences = t.Count(d => t.Key.CodeID == d.CodeID),
    // taking original items with us
    Items = t
};

Having done that, your final query (including occurrences filter) might look like this:

var result = summary
    // get all necessary data, including filter that matched given item
    .Select(Item => new
        {
            Item,
            Filter = searches.FirstOrDefault(f => f.CodeID == Item.CodeID)
        })
    // get rid of those without matching filter
    .Where(i => i.Filter != null)
    // this is your occurrences filtering
    .Where(i => i.Item.Occurences >= i.Filter.MinOccurences
        && i.Item.Occurences <= i.Filter.MaxOccurences)
    // and finally extract original events IDs
    .SelectMany(i => i.Item.Items)
    .Select(i => i.ID);

This produces 1 , 2 as result. 3 and 4 are left out as they don't get past occurrences filtering.

I have run your program in linqpad.

My understanding is that you want to filter using filter.MinOccurences and filter.MaxOccurences on Occurences count of result data set.

You can add additional filters using Where clause.

if (filter.MinOccurences.HasValue)
 summary = summary.Where (x=> x.Occurences >= filter.MinOccurences);

if (filter.MaxOccurences.HasValue)
 summary = summary.Where (x=> x.Occurences <= filter.MaxOccurences);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM