Reduce execution time in two foreach loop - c#

//Get linked claim for children list from ClaimLink
foreach (var claim in processedClaims)
{
if (claim.Children == null)
{
claim.Children = new List<Claim>();
}
var claimRelationList = newClaimLink.Where(k=> k.ClaimLinkId == claim.Id).ToList();
if (claimRelationList.Any())
{
//Get the claim for all selected ClaimLink
foreach (var claimLink in claimRelationList)
{
var newChildren = claims.Where(p => p.Id == claimLink.ClaimId).ToList();
claim.Children = claim.Children != null && claim.Children.Any() ? newChildren.Concat(claim.Children) : newChildren;
}
}
}
I want to reduce the execution time in above two foreach loop. Is there have better way than this?

You can remove the if (claimRelationList.Any()) condition as the following foreach will not iterate over an empty list.
You already know that claim.Children is not null within that for each because you initialized it above. So I would replace
claim.Children = claim.Children != null && claim.Children.Any() ? newChildren.Concat(claim.Children) : newChildren;
With
claim.Children.AddRange (newChildren).

Multiple options i see at first sight:
don't call ToList(), not necessary here unless you have some sort of query executing in the background, see comment of #Shelby115
newClaimLink.Where(k=> k.ClaimLinkId == claim.Id) could be replaced by a dictionary created beforehand like newClaimLink.ToDictionary(k=> k.ClaimLinkId)
same goes for claims.Where(p => p.Id == claimLink.ClaimId)

Here is my best answer for above question.
//Get linked claim for children list from ClaimLink
foreach (var claim in processedClaims)
{
if (claim.Children == null)
{
claim.Children = new List<Claim>();
}
var claimRelationList = newClaimLink.Where(k=> k.ClaimLinkId == claim.Id).ToList();
if (claimRelationList.Any())
{
//Get the claim for all selected ClaimLink
var newChildren = claims.Where(o => claimRelationList.Select(p => p.ClaimId).Contains(o.Id));
if (newChildren.Any())
{
claim.Children = claim.Children != null && claim.Children.Any() ? newChildren.Concat(claim.Children) : newChildren;
}
}
}
I resolved my problem with lambda expression for the inner foreach loop then I could have to reduce the execution time more than 250ms.
Thank you for the helping me

Related

Change loop to Linq C#

One of my function in C# required me to filter some value.So, I try it by using a lot of loop in it. It works but doesn't look like effecient.Any idea on how to convert code below to LinQ?
Any help is appreciated.
var object1 = JsonConvert.DeserializeObject<List<string>>(object1json);
foreach (var item1 in table1)
{
if (item1.Code == InputCode)
{
for (int i = 0; i < object1.Count(); i++)
{
tempData temp = new tempData();
foreach (var item2 in item1.List)
{
if (item2.Code == object1[i])
{
temp.Code = item2.Code;
temp.Description = item2.Description;
}
}
if(temp.Code != null || temp.Description != null)
final.Add(temp);
}
}
}
If you want your code to be more efficient, as pointed out in the comments, converting it to Linq isn't really going to help. It's still the same logic, just written in a different way. If you're going for readability, it can be improved with just a few changes:
foreach (var item1 in table1.Where(i => i.Code == InputCode))
{
foreach (var code in object)
{
// This could be SingleOrDefault, I don't know if you have duplicates in the list or not
var item2 = item1.List.LastOrDefault(i => i.Code == code);
if(item2 != null)
{
final.Add(new tempData
{
Code = item2.Code,
Description = item2.Description,
});
}
}
}
If you convert the whole thing to Linq:
var final = table1.Where(i => i.Code == InputCode)
.SelectMany(item1 => object.Select(code => item1.List.LastOrDefault(i => i.Code == code))
.Where(item2 => item2 != null)
.Select(item2 => new tempData
{
Code = item2.Code,
Description = item2.Description,
})
.ToList();
Personally, I prefer the first option, as it's a bit easier to read.
I guess what you post is sample code instead of actual code otherwise it would be better to avoid keyword like object in C#. Anyway,
var final = table1.Where(item1 => item1.Code == InputCode)
.SelectMany(item1 => item1.List)
.Where(item2 => #object.Contains(item2.Code))
.Where(temp => temp.Code != null || temp.Description != null)
.Select(item2 => new tempData()
{
Code = item2.Code,
Description = item2.Description
});

How to optimize this create/update/delete comparisons?

I'm with EF 6.
I have a list of currents item in the db, which I retrieve with:
ae_alignedPartners_olds = ctx.AE_AlignedPartners.AsNoTracking().ToList(); // list of List<AE_AlignedPartners>
Than, I retry the same objects from a JSON, with:
ae_alignedPartners_news = GetJSONListObjects(); // list of List<AE_AlignedPartners>
Than I'm making some comparisons (to see which I need to update, which I need to delete and which to create. That's the current code:
// intersection
var IDSIntersections = (from itemNew in ae_alignedPartners_news
join itemOld in ae_alignedPartners_olds on itemNew.ObjectID equals itemOld.ObjectID
select itemNew).Select(p => p.ObjectID).ToList();
// to update
IList<AE_AlignedPartners> ae_alignedPartners_toUpdate = new List<AE_AlignedPartners>();
foreach (var item in IDSIntersections)
{
var itemOld = ae_alignedPartners_olds.First(p => p.ObjectID == item);
var itemNew = ae_alignedPartners_news.First(p => p.ObjectID == item);
if (itemOld.Field1 != itemNew.Field1 ||
itemOld.Field2 != itemNew.Field2 ||
itemOld.Field3 != itemNew.Field3 ||
itemOld.Field4 != itemNew.Field4 ||
itemOld.Field5 != itemNew.Field5 ||
itemOld.Field6 != itemNew.Field6 ||
itemOld.Field7 != itemNew.Field7 ||
itemOld.Field8 != itemNew.Field8 ||
itemOld.Field9 != itemNew.Field9)
{
itemOld.Field1 = itemNew.Field1;
itemOld.Field2 = itemNew.Field2;
itemOld.Field3 = itemNew.Field3;
itemOld.Field4 = itemNew.Field4;
itemOld.Field5 = itemNew.Field5;
itemOld.Field6 = itemNew.Field6;
itemOld.Field7 = itemNew.Field7;
itemOld.Field8 = itemNew.Field8;
itemOld.Field9 = itemNew.Field9;
ae_alignedPartners_toUpdate.Add(itemOld);
}
}
// to create
IList<AE_AlignedPartners> ae_alignedPartners_toCreate = ae_alignedPartners_news.Where(p => !IDSIntersections.Contains(p.ObjectID)).ToList();
// to delete
IList<AE_AlignedPartners> ae_alignedPartners_toDelete = ae_alignedPartners_olds.Where(p => !IDSIntersections.Contains(p.ObjectID)).ToList();
Which is faster enough for 1000~ records. Over 50k, it becomes very very slow.
What do you suggest to improve the whole?
If you want to find out what's slow I suggest profiling or simply pausing the debugger 10 times to see where it stops most often (you can try that with your existing code). But here I could spot the problem immediately:
var itemOld = ae_alignedPartners_olds.First(p => p.ObjectID == item);
var itemNew = ae_alignedPartners_news.First(p => p.ObjectID == item);
This is scanning the entire list which is O(N). Together with the outer loop this becomes O(N^2).
The best solution would be to restructure your query so that these lookups are not necessary. It seems to me that the join already outputs the objects you need.
But you can also use a hash table to speed up the lookups.
var dict_ae_alignedPartners_olds = ae_alignedPartners_olds.ToDictionary(p => p.ObjectID);
var dict_ae_alignedPartners_news = ae_alignedPartners_news.ToDictionary(p => p.ObjectID);
foreach (var item in IDSIntersections)
{
var itemOld = dict_ae_alignedPartners_olds[item];
var itemNew = dict_ae_alignedPartners_news[item];
//...
}

Remove a range of items from a list without looping

Hi Is there a more ellegant way of doing this must I do the loop is there like a range funciton I could just remove all the items found
Sorry I should have showing how my qry is being inserted.
Btw these are two different entities that I am removing from I hope you get the idea.
var qry = db.AssemblyListItems.AsNoTracking().Where(x =>
x.ProductionPlanID == (long)_currentPlan.ProductionPlan ).ToList();
var hasbeenAssembled = db.CompletedPrinteds.AsNoTracking().Where(x =>
x.ProductionPlanId == item.ProductionPlanID).ToList();
var hasbeenFound = db.CompletedPrinteds.AsNoTracking().Where(x =>
x.ProductionPlanId== item.ProductionPlanID).ToList();
foreach (var subitem in hasbeenAssembled )
{
if(item.ProductionPlanID ==subitem.ProductionPlanId && item.DocumentNo == subitem.DocumentNo && item.DocumentNo == subitem.DocumentNo && item.OutstandingToMake ==0)
{
qry.RemoveAll(x => x.ProductionPlanID == subitem.ProductionPlanId && x.DocumentNo == item.DocumentNo && x.ItemCode == subitem.StockCode && item.OutstandingToMake ==0);
}
}
public List<AssemblyListItems> RemoveDespatchedItems(List<AssemblyListItems> AssemblyItems)
{
foreach (AssemblyListItems item in AssemblyItems)
{
using (var db = new LiveEntities())
{
var hasNotBeenDespatched = db.PalletizedItems.Where(w => w.Despatched != "Not Despatched");
foreach (var subitem in hasNotBeenDespatched)
{
AssemblyItems.RemoveAll(x => x.ProductionPlanID == subitem.ProductionPlanID && x.DocumentNo == item.DocumentNo && x.ItemCode == subitem.StockCode);
}
}
}
return AssemblyItems;
}
I just need to remove the items from the first query hasNotBeenDespatched from the second query.As could be over 400 items i want it to be efficient as possible.
Edit 2
I am a we bit closer thanks buts its still not removing the items from the removedespatchitems from the assebmittems I do not no why
public List<AssemblyListItems> RemoveDespatchedItems(List<AssemblyListItems> AssemblyItems, Int64 ProductionPlanId)
{
using (var db = newLiveEntities())
{
List<PalletizedItems> removeDespatchItems = db.PalletizedItems.Where(w => w.Despatched != "Not Despatched" && w.ProductionPlanID == ProductionPlanId).ToList();
var itemsDocumentNo = db.PalletizedItems.Select(x => x.ProductionPlanItemID).ToList();
foreach (var subitem in removeDespatchItems) {
AssemblyItems.RemoveAll(x => x.ProductionPlanID == subitem.ProductionPlanID && itemsDocumentNo.Contains(x.ProductionPlanItemID) && x.ItemCode == subitem.StockCode && x.LineQuantity==x.AllocatedQuantity);
}
}
return AssemblyItems;
}
Not 100% I get exactly how it should be.
However in general you could use join that would result in it being done in the database. Something like this:
var remainingItems = (from ali in db.FUEL_AssemblyListItems
join completed in db.FuelCompletedPrinteds
on new { ali.ProductionPlanID, ali.DocumentNo, ali.ItemCode } equals new { completed.ProductionPlanID, completed.DocumentNo, completed.StockCode }
join dispatched in db.FUEL_PalletizedItems
on new { ali.ProductionPlanID, ali.DocumentNo, ali.ItemCode } equals new { dispatched.ProductionPlanID, dispatched.DocumentNo, dispatched.StockCode }
where (ali.ProductionPlanID == (long) _currentPlan.ProductionPlan
&& ali.DocumentNo == completed.DocumentNo
&& completed.OutstandingToMake == 0
&& dispatched.Despatched != "Not Despatched")
select ali).ToList();
Depending upon the records in the database the join might need to be a outer join which needs a slightly different syntax but hopefully you've got a starting point.

Error while removing item from collection

Getting error Collection was modified; enumeration operation may not execute.
var toUpdateItm = MC_CRM_T001A.ItemDetails
.Where(X => X.CatNo == SelectedCRM_T001A.CatNo);
foreach (var itm in toUpdateItm)
{
int x = MC_CRM_T001A.PartDetails.IndexOf(MC_CRM_T001A.PartDetails
.Where(X => X.cat_item_id == itm.id)
.FirstOrDefault()
);
if (x >= 0 && x!=null)
{
MC_CRM_T001A.PartDetails.RemoveAt(x);
}
}
foreach (var itm in toUpdateItm)
{
if (itm.CatNo == SelectedCRM_T001A.CatNo)
{
MC_CRM_T001A.ItemDetails.Remove(itm);
}
}
You can't modify the list you're looping over. Change for foreach calls to foreach (var itm in toUpdateItem.ToList()), which will create a copy of the list, instead.
Also, you can express this code more cleanly without all the IndexOf stuff:
var toUpdateItm = MC_CRM_T001A.ItemDetails.Where(X => X.CatNo == SelectedCRM_T001A.CatNo).ToList();
foreach (var itm in toUpdateItm.ToList())
{
var item = MC_CRM_T001A.PartDetails.FirstOrDefault(X => X.cat_item_id == itm.id);
if (item != null) { MC_CRM_T001A.PartDetails.Remove(item); }
if (itm.CatNo == SelectedCRM_T001A.CatNo) { MC_CRM_T001A.ItemDetails.Remove(itm);
}
You don't need two loops either.

Converting foreach into single pass Linq Group By

I have a collection of items that may have one or more properties.
I want to use a Linq statement (query syntax) to filter and group that list in a single pass such that it checks for a mandatory property first, and then looks for an optional property.
I can't figure out how to do this without first filtering the list and then going over it again to look for the optional property.
Here's how I could do it in a foreach statement. (Not efficient, just illustrating what I need.)
var usefulBoxes = new Dictionary<Box, int>;
foreach (box in cart)
{
bool boxNeeded = false;
int someCounter = 0;
foreach (prop in box.Properties)
{
if (prop == neededProp)
boxNeeded = true;
else if (boxNeeded && prop == optionalProp)
someCounter += 1;
}
if (boxNeeded)
usefulBoxes.Add(box, someCounter)
}
var usefulBoxes= box.where(b=>b.boxProperties.prop==neededProp).ToList();
this your demo linq:
var usefulBoxes = new Dictionary<List<int>, int>();
foreach (var boxNeeded in from box in cart let boxNeeded = false let someCounter = 0 from prop in box.Properties.Where(prop => prop == neededProp) select boxNeeded)
{
if (prop == neededProp)
boxNeeded = true;
else if (boxNeeded && prop == optionalProp)
someCounter += 1;
if (boxNeeded)
usefulBoxes.Add(box, someCounter);
}

Categories

Resources