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.
Related
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
});
I have several foreach chained.
I would like to know if it is possible to do a linq synthesis of these foreach.
foreach(var item in parameter)
{
foreach (var worksheets in item.Item6.Worksheets)
{
var worksheetName = worksheets.Value.Name;
foreach (var dynamicRange in worksheets.Value.DynamicRanges)
{
var dynamicRangeRowStartIndex = dynamicRange.RowStartIndex;
var dynamicRangeColumnStartIndex = dynamicRange.ColumnStartIndex;
foreach (var node in dynamicRange.Nodes)
{
var nodeDirection = node.Direction;
foreach (var rule in node.Rules)
{
reportWorkbook.Worksheets.Values.First(c => c.Name == worksheetName).DynamicRanges.First(c => c.RowStartIndex == dynamicRangeRowStartIndex && c.ColumnStartIndex == dynamicRangeColumnStartIndex)
.NodeByDirection(nodeDirection).Rules[ruleCount].DefinitionRule.ContextItem = item.Item6.Worksheets.Values.First(c => c.Name == worksheetName).DynamicRanges.First(c => c.RowStartIndex == dynamicRangeRowStartIndex && c.ColumnStartIndex == dynamicRangeColumnStartIndex)
.NodeByDirection(nodeDirection).Rules[ruleCount].DefinitionRule.ContextItem;
ruleCount++;
}
ruleCount = 0;
}
}
}
}
Do you have any idea how to do this?
Thank you
I have a couple of problems with my below code.
var groupedItems = inputFiles.GroupBy(q => q.Name.ToLower().Split('_').ElementAt(2));
string currentNo = ////value retreived from someMethod;
if (string.IsNullOrEmpty(currentNo))
{
if (groupedItems.Count() > 1)
{
foreach (var group in groupedItems)
{
foreach (var groupedItem in group)
{
ErrorFile(groupedItem);
}
}
}
else if (groupedItems.Count() == 1)
{
ProcessFile();
}
}
else
{
foreach (var group in groupedItems.Where(x => x.Key != currentNo))
{
foreach (var groupedItem in group)
{
ErrorFile(groupedItem);
}
}
}
There is repetitive code with nested foreach loops. I am looking at possibility of optimization
How do I handle when Split and ElementAt(2) return errors. I still need to call ErrorFile() method even if I am unable to Split by _.
Hard to understand what your code is really doing without more context, but this should be about right:
static void SomeMethod(IEnumerable<File> inputFiles)
{
var groupedItems = inputFiles.GroupBy
(f => splitAndFindElement(f.Name, '_', 2, string.Empty));
string currentNo = //whatever;
if (string.IsNullOrEmpty(currentNo))
{
if (groupedItems.Count() > 1)
{
foreach (var item in groupedItems.SelectMany(g => g))
{
ErrorFile(item);
}
}
else if (groupedItems.Count() == 1)
{
ProcessFile();
}
}
else
{
foreach (var item in groupedItems.Where(g => g.Key != currentNo).SelectMany(g => g))
{
ErrorFile(item);
}
}
}
And the helper method:
static string splitAndFindElement(string input, char splitter, int index, string resultOnFail)
{
var succesful = false;
string[] words = null;
if (input != null)
{
words = input.Split(splitter);
succesful = words.Length > index;
}
return succesful ? words[index] : resultOnFail;
}
The trick here is to group files with key "" if they can't be split. This will ensure that they will be processed with ErrorFile either becuase there is more than one grouping or because the key does not equal currentNo. I'm assuming here that Name can't end in "_".
Also, SelectMany is used to flatten enumerables of enumerables and avoid nested loops.
We can optimize repeated loops as following, and to handle error use try catch
var groupedItems;
try
{
groupedItems= inputFiles.GroupBy(q => q.Name.ToLower().Split('_').ElementAt(2));
string currentNo = ////value retreived from someMethod;
if (string.IsNullOrEmpty(currentNo) && groupedItems.Count() == 1)
{
ProcessFile();
}
else
{
foreach (var group in groupedItems.Where(x => string.IsNullOrEmpty(currentNo) || x.Key != currentNo))
{
foreach (var groupedItem in group)
{
ErrorFile(groupedItem);
}
}
}
}
catch
{
ErrorFile(groupedItem);
}
I cannot wrap my head around why this code generates a lot of null-lines:
int nr = 0;
foreach (var item in lists.Select(x => x.match_id))
{
foreach (var match in lists)
{
Console.Write(match.nickname
.Where(x => lists[nr].match_id == match.match_id)
.Select(z => match.nickname)
.FirstOrDefault());
}
nr++;
}
lists is an array of objects. The output:
Below all of the nulls are the next records, and then more nulls etc. Why?
To not output the null, you need to not call Console.Write if the value is null.
Try something like this:
int nr = 0;
foreach (var item in lists.Select(x => x.match_id))
{
foreach (var match in lists)
{
var n = match.nickname
.Where(x => lists[nr].match_id == match.match_id)
.Select(z => match.nickname)
.FirstOrDefault();
if (n != null)
{
Console.Write(n);
}
}
nr++;
}
You are looping twice through the same Array. I think you want to do:
for(int i = 0; i < lists.length; i++){
Console.Write(item.nickname
.Where(x=> lists[i].match_id === item.match_id)
.Select( z=> item.nickname)
.FirstOrDefault());
}
My 2 cents after a quick look..
I have the following:
foreach ( var menuItem in Model.menuItems) {
if (menuItem.Type == "02") {
}
}
The code within the foreach executes only when Type==2 so is there some way that I could add the check for Type == "02" into the top part of my loop.
List(Of T).ForEach Method : Performs the specified action on each element of the List(Of T).
Model.menuItems.Where(c => c.Type == "02").ToList().ForEach(delegate(type data)
{
//code to execute
});
Try
foreach ( var menuItem in Model.menuItems.Where(mi => mi.Type == "02") {
}
If your menuItems already are enumerable:
foreach (var menuItem in Model.menuItems.Where(c => c.Type == "02")) {
}
Otherwise try:
foreach (var menuItem in Model.menuItems.AsEnumerable().Where(c => c.Type == "02")) {
}
Or:
foreach (var menuItem in Model.menuItems.ToList().Where(c => c.Type == "02")) {
}
LINQ:
foreach (var menuItem in Model.menuItems.Where(item => item.Type == "02")) {
}
You can do this with linq using the following:
foreach ( var menuItem in Model.menuItems.Where(m=>m.Type == "02") {
}
do
foreach ( var menuItem in Model.menuItems.Where(m => m.Type == "02")) {}
how about
foreach ( var menuItem in Model.menuItems.Where(m => m.Type == "02")) {
}