Is there any efficient way to optimize following Linq - c#

private static List<Patient> GetPatientData()
{
return new List<Patient>()
{
new Patient(1,new List<Case>() { new Case(10, CaseType.ambulant)}),
new Patient(2,new List<Case>() { new Case(20, CaseType.ambulant), new Case(21, CaseType.ambulant), new Case(22, CaseType.stationaer),new Case(23, CaseType.teilstat) }),
new Patient(3,new List<Case>() { new Case(30, CaseType.ambulant), new Case(31, CaseType.ambulant), new Case(32, CaseType.stationaer), new Case(33, CaseType.stationaer), new Case(34, CaseType.teilstat) }),
new Patient(4,new List<Case>() { new Case(40, CaseType.ambulant), new Case(41, CaseType.stationaer), new Case(43, CaseType.teilstat), new Case(44, CaseType.ambulant), new Case(45, CaseType.stationaer), new Case(46, CaseType.teilstat) }),
new Patient(5,new List<Case>() {new Case(53, CaseType.teilstat),new Case(54, CaseType.teilstat) })
};
}
List<Patient> patientList = GetPatientData();
var result = patientList.SelectMany(item => item.CaseList.Select(itemCase=> itemCase.CaseType).Distinct());
foreach (CaseType item in result)
{
Console.WriteLine("CaseTypes = {0}",item);
}
The above code gives the list of Patient, from that I want a distinct case list. So is there any optimized way to do this from what I have done?
Dictionary<int, int> result = patientList.ToDictionary(item => item.PatientID , item=> item.CaseList.Select(x=>x.CaseType).Distinct().Count());
foreach (KeyValuePair<int,int> item in result)
{
Console.WriteLine("{0} {1}", item.Key,item.Value);
}
In Second case i am trying to get patientID & Distinct CaseType Count for that particular patient. can i optimize this one.

To get the distinct case types of all patients in that list, I used
var result =
(
from patient in patientList
from typ in patient.CaseList
select typ.CaseType
).Distinct();
foreach (var item in result)
{
Console.WriteLine("CaseTypes = {0}", item);
}
Of course, you could rewrite it method-style.
To get the same distinct case list, but per patient, try this:
var result =
(
from patient in patientList
group patient by patient.PatientID into g
from patient in g
from typ in patient.CaseList
select new { ID = g.Key, Type = typ.CaseType }
).Distinct();
foreach (var item in result)
{
Console.WriteLine("Patient {0} has the following case: {1}", item.ID, item.Type);
}
An alternative (less repeating) way to present the results is to group:
var result =
(
from patient in patientList
group patient by patient.PatientID into g
from p in g
select new { g.Key, List = p.CaseList.Select(c => c.CaseType).Distinct() }
).ToDictionary(kv => kv.Key, kv => kv.List);
foreach (var item in result)
{
Console.WriteLine("Patient {0} has the following cases:", item.Key);
foreach (var type in item.Value)
{
Console.WriteLine("\t{0}", type);
}
}

You are missing a last Distinct:
var result = patientList.SelectMany(item => item.CaseList.Select(itemCase => itemCase.CaseType).Distinct()).Distinct();
There's nothing to improve there.
You could choose to keep only the last Distinct (so there's one less Enumerator to be created) but having it gives you less results yielded out to your final Distinct. Performance then would change depending on the amount of data (number of Patients and Cases per Patient) though nothing that would make any difference.

Related

Make two foreach statements into one

I have the following foreach statements, which I want to transfer into linq query.
var equalityGroup= new Dictionary<string, List<string>();
var firstGroup = new Dictionary<string, List<string>();
var request = new List<Request>();
foreach(var element in request)
{
var key = element.Number;
if (!equalityGroup.ContainsKey(key))
{
equalityGroup.Add(key, new List<string>());
}
foreach(var item in firstGroup)
{
var query = item.Value.FindAll(y => y ==element.Id);
if (query.Any())
{
equalityGroup[key].AddRange(query);
}
}
}
Can someone give me a good example for Linq that will work as this foreaches?
I think you should be able to greatly increase the performance for large collections by performing a join (which uses hash-tables internally) instead of nested loops.
var firstGroup = new Dictionary<string, List<string>>();
var request = new List<Request>();
var q = from element in request
join y in firstGroup.SelectMany(x => x.Value) on element.Id equals y
group y by element.Id into g
select new { g.Key, g };
var equalityGroup = q.ToDictionary(x => x.Key, x => x.g.ToList());

Filter and add values using C# using lambda expression

New to C# and appreciate any help. The issue is that I need to filter the results of my api call against an array (using an "allowedA" and "allowedB" array.) I don't know how to edit the lambda expression to check against the loop.
var activities = await _restClientTaxonomy.GetTaxonomyFullAsync(TAXONOMY_CLASSIFICATIONID_FOR_ACTIVITY);
var activityTypes = await _restClientTaxonomy.GetTaxonomyFullAsync(TAXONOMY_CLASSIFICATIONID_FOR_ACTIVITY_TYPES);
var documentEventxx = activities.Select(type => type.Id);
long [] allowedA = new long []{ 7137, 40385637};
long [] allowedB = new long []{ 7137, 40385637};
foreach (long value in documentEventxx)
{
foreach (var item in allowed)
{
if (item == value) {
//These are the values I am looking for -> values that are part of the documentEventxx and allowedB.
}
}
}
var result = activityTypes.Select(type => new CategoryViewModel
{
Id = type.Id,//This is where I want to add only items that are in the allowedA array
Text = type.Name,
Types = activities.Where(a => a.ParentId == type.Id).Select(t => new TaxonomyMemberTextItem
{
Id = t.Id, //This is where I want to add only items that are in the allowedB array
Text = t.Name
}).ToList()
}).ToArray();
I have been reading about lambda expressions and foreach loops so please don't just post a random link.
Thanks in advance.
Filter the values before Selecting.
activityTypes.Where(x=>allowedA.Contains(x.Id)).Select(type => new CategoryViewModel
{
Id = type.Id,
Text = type.Name,
Types = activities.Where(a => a.ParentId == type.Id && allowedB.Contains(a.Id)).Select(t => new TaxonomyMemberTextItem
{
Id = t.Id,
Text = t.Name
}).ToList()
})
To filter you use .Where. You .Select to create a list of new types. So in order to filter, then create the lists of objects you want:
var result = activityTypes.Where(type=>isAllowed(type.Id)).Select(type => new CategoryViewModel
{
Id = type.Id,//This is where I want to add only items that are in the allowedA array
Text = type.Name,
Types = activities.Where(a => a.ParentId == type.Id&&isAllowed(a.Id)).Select(t => new TaxonomyMemberTextItem
{
Id = t.Id, //This is where I want to add only items that are in the allowedB array
Text = t.Name
}).ToList()
}).ToArray();

How to break or Exit from Linq query

I need to find the 1st matching record between 2 generic Lists.
Below is the code I have written. But, even after a match is obtained, it is still processing further records. Is there any way to break from the below query? Something like "break" or "Exit for"
foreach (var itemOld in oldList)
{
var result = (from itemNew in newList
where itemNew.Id == itemOld.Id
select itemNew).ToList();
// Do processing
}
The keyword you are looking for is break;
foreach (var itemOld in oldList)
{
var result = (from itemNew in newList
where itemNew.Id == itemOld.Id
select itemNew).ToList();
if(someThingisTrue){
break;
}
}
You can do this. Both the inner and outer query will terminate as soon as a matching item is found:
foreach (var itemOld in oldList)
{
var result = (from itemNew in newList
where itemNew.Id == itemOld.Id
select itemNew).FirstOrDefault();
if (result != null)
{
// process result
break;
}
}
You can also combine them like this:
var result = (from itemNew in newList
from itemOld in oldList
where itemNew.Id == itemOld.Id
select itemNew).FirstOrDefault();
if (result != null)
{
// process result
}
The difference is that the first approach will go through newList repeatedly until a match is found. The second will go through oldList repeatedly until a match is found.
To address your comment below, if you want to process all the matches, just remove the break from the first example, or do this for the second version:
var results = from itemNew in newList
from itemOld in oldList
where itemNew.Id == itemOld.Id
select itemNew;
foreach (var item in results)
{
// process item
}
Just for fun.
var predicate = itemNew => oldList.FirstOrDefault(itemOld => itemOld.Id == itemNew.Id);
var result = newList.FirstOrDefault(predicate);
EDIT: as pointed out in the comments, I did stuff in the wrong order =(. Fixed for correctness.
A little bit late, but you can do it with a join:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public class Item
{
public int Id { get; set; }
}
public static void Main()
{
var oldList = new List<Item>();
oldList.Add(new Item() { Id = 3 });
oldList.Add(new Item() { Id = 4 });
oldList.Add(new Item() { Id = 5 });
var newList = new List<Item>();
newList.Add(new Item() { Id = 1 });
newList.Add(new Item() { Id = 2 });
newList.Add(new Item() { Id = 3 });
// here is the linq join
var result =
newList.Join(
oldList,
item => item.Id,
item => item.Id,
(itemNew, itemOld) => itemNew).FirstOrDefault();
// outputs 3
Console.WriteLine(result.Id);
}
}

Nested Group by LINQ

I am unable to solve this problem with the LINQ Query.
So we have the table structure as follows:
Id || bug_category || bug_name || bug_details || bug_priority
I want to group by bug_category first. For each bug_category, I want to in turn group by bug__priority.
So basically I want something like :
bug_category = AUDIO :: No of BUGS --> Critical = 3, Medium = 2 and Low = 7 bugs.
bug_category = VIDEO :: No of BUGS --> Critical = 5, Medium = 1 and Low = 9 bugs.
The below query returns all unique combinations of category AND customer_priority:
(where RawDataList is simply a List of data which has the above mentioned structure )
var ProceesedData = from d in RawDataList
group d by new { d.bug_category, d.bug_priority } into g
select new
{
g.Key.bug_category,
g.Key.bug_priority
};
The below query returns the category followed by a list of records in that category:
var ProceesedData = from d in RawDataList
group d by d.bug_category into g
select new { g.Key, records = g
};
But I am unable to proceed further as ProcessedData(the return variable) is an unknown type. Any thoughts on this?
This is an easier way to accomplish nested groupings. I've tested it for in memory collections, whether or not your particular DB provider will handle it well might vary, or whether it performs well is unknown.
Assuming you had two properties, and wanted to group by both State and Country:
var grouped = People
.GroupBy(l => new { l.State, l.Country})//group by two things
.GroupBy(l=> l.Key.Country)//this will become the outer grouping
foreach(var country in grouped)
{
foreach(var state in country)
{
foreach(var personInState in state)
{
string description = $"Name: {personInState.Name}, State: {state.StateCode}, Country: {country.CountryCode}";
...
}
}
}
I suspect you want (names changed to be more idiomatic):
var query = from bug in RawListData
group bug by new { bug.Category, bug.Priority } into grouped
select new {
Category = grouped.Key.Category,
Priority = grouped.Key.Priority,
Count = grouped.Count()
};
Then:
foreach (var result in query)
{
Console.WriteLine("{0} - {1} - {2}",
result.Category, result.Priority, result.Count);
}
Alternatively (but see later):
var query = from bug in RawListData
group bug by new bug.Category into grouped
select new {
Category = grouped.Category,
Counts = from bug in grouped
group bug by grouped.Priority into g2
select new { Priority = g2.Key, Count = g2.Count() }
};
foreach (var result in query)
{
Console.WriteLine("{0}: ", result.Category);
foreach (var subresult in result.Counts)
{
Console.WriteLine(" {0}: {1}", subresult.Priority, subresult.Count);
}
}
EDIT: As noted in comments, this will result in multiple SQL queries. To obtain a similar result structure but more efficiently you could use:
var dbQuery = from bug in RawListData
group bug by new { bug.Category, bug.Priority } into grouped
select new {
Category = grouped.Key.Category,
Priority = grouped.Key.Priority,
Count = grouped.Count()
};
var query = dbQuery.ToLookup(result => result.Category,
result => new { result.Priority, result.Count };
foreach (var result in query)
{
Console.WriteLine("{0}: ", result.Key);
foreach (var subresult in result)
{
Console.WriteLine(" {0}: {1}", subresult.Priority, subresult.Count);
}
}
I think you're searching something like that:
var processedData =
rawData.GroupBy(bugs => bugs.bug_category,
(category, elements) =>
new
{
Category = category,
Bugs = elements.GroupBy(bugs => bugs.bug_priority,
(priority, realbugs) =>
new
{
Priority = priority,
Count = realbugs.Count()
})
});
foreach (var data in processedData)
{
Console.WriteLine(data.Category);
foreach (var element in data.Bugs)
Console.WriteLine(" " + element.Priority + " = " + element.Count);
}
You can do it like this
var retList = (from dbc in db.Companies
where dbc.IsVerified && dbc.SellsPCBs && !dbc.IsDeleted && !dbc.IsSpam && dbc.IsApproved
select new
{
name = dbc.CompanyName,
compID = dbc.CompanyID,
state = dbc.State,
city = dbc.City,
businessType = dbc.BusinessType
}).GroupBy(k => k.state).ToList();
List<dynamic> finalList = new List<dynamic>();
foreach (var item in retList)
{
finalList.Add(item.GroupBy(i => i.city));
}

Grouping KeyValue pairs to Dictionary

I have the following code :
using System.Collections.Generic;
public class Test
{
static void Main()
{
var items = new List<KeyValuePair<int, User>>
{
new KeyValuePair<int, User>(1, new User {FirstName = "Name1"}),
new KeyValuePair<int, User>(1, new User {FirstName = "Name2"}),
new KeyValuePair<int, User>(2, new User {FirstName = "Name3"}),
new KeyValuePair<int, User>(2, new User {FirstName = "Name4"})
};
}
}
public class User
{
public string FirstName { get; set; }
}
Above as you can see there are multiple users for same key . Now I want to group them and convert the list object to dictionary in which the Key will be the same(1,2 as shown above) but the value will be the collection.Like this:
var outputNeed = new Dictionary<int, Collection<User>>();
//Output:
//1,Collection<User>
//2,Collection<User>
i.e they are grouped now.
How can I achieve that ?
I would suggest you use a Lookup<TKey, TElement> instead. This data-structure is specifically intended for use as a map from keys to collections of values.
//uses Enumerable.ToLookup: the Id is the key, and the User object the value
var outputNeeded = items.ToLookup(kvp => kvp.Key, kvp => kvp.Value);
Of course, if you do need the dictionary (to allow for mutability perhaps), you can do something like:
var outputNeeded = new Dictionary<int, Collection<User>>();
foreach(var kvp in list)
{
Collection<User> userBucketForId;
if(!outputNeeded.TryGetValue(kvp.Key, out userBucketForId))
{
// bucket doesn't exist, create a new bucket for the Id, containing the user
outputNeeded.Add(kvp.Key, new Collection<User> { kvp.Value });
}
else
{ // bucket already exists, append user to it.
userBucketForId.Add(kvp.Value);
}
}
On another note, theCollection<T>class is not all that useful unless you intend to subclass it. Are you sure you don't just need aList<User>?
Here's an example using LINQ's ToDictionary:
var output = items.GroupBy(kvp => kvp.Key)
.ToDictionary(group => group.Key,
group => group.Select(kvp => kvp.Value).ToList());
It results in a Dictionary<int,List<User>>.
Given your initial variable "item", and the suggested output variable "outputNeed", this is what you need to do:
NOTE:This is not actual c#/vb code, so please change this pseudo as required (I don't have a VS Studio at hand at the moment):
foreach (KeyValuePair<int, User> pair in items)
{
//add a new collection if this is the first time you encounter the key
if (! outputNeed.Contains(pair.Key)
{
outputNeed[pair.Key] = new ArrayList<User>());
}
//add the user to the matching collection
outputNeed.Add(pair.Key, pair.User);
}
Good luck
foreach (var u in items)
{
if (outputNeed.Contains(u.Key)) {
outputNeed[u.Key].Add(u.Value);
}
else {
Collection<User> a=new Collection<User>();
a.Add(u.Value);
outputNeed.Add(u.Key,a);
}
}
Here is my solution :
var items = new List<KeyValuePair<int, User>>
{
new KeyValuePair<int, User>(1, new User { FirstName = "Name1" }),
new KeyValuePair<int, User>(1, new User { FirstName = "Name2" }),
new KeyValuePair<int, User>(2, new User { FirstName = "Name3" }),
new KeyValuePair<int, User>(2, new User { FirstName = "Name4" })
};
var result = (
from item in items
group item.Value by item.Key into grouped
select grouped
).ToDictionary(g => g.Key, g => g);
DotNetFiddle

Categories