recursive loop is updating all properties in generic list - c#

I have the following method it creates a generic list recursively. I'm getting some interesting results. The property CurrentAllocation is always overwritten with the last value.
Here is the line in question.
courierTypeRegion.CurrentAllocation = remaining;
courierTypeRegionOutput.Add(courierTypeRegion);
Here is the whole method
public static List<CourierTypeRegion> FindClosest2(decimal quantity, decimal remaining, ICollection<CourierTypeRegion> courierTypeRegions, List<CourierTypeRegion> courierTypeRegionOutput)
{
var processed = false;
var courierOrderByDesc = courierTypeRegions.OrderByDescending(x => x.CourierType.PalletsPerTrailer).ToList();
var courierCount = courierOrderByDesc.Count();
var courierCurrent = 0;
foreach (var courierTypeRegion in courierOrderByDesc)
{
if (remaining >= courierTypeRegion.CourierType.PalletsPerTrailer && !processed)
{
courierTypeRegion.CurrentAllocation = courierTypeRegion.CourierType.PalletsPerTrailer;
courierTypeRegionOutput.Add(courierTypeRegion);
processed = true;
}
if (!processed)
{
if (courierOrderByDesc[courierCurrent + 1] != null)
{
if (remaining > courierOrderByDesc[courierCurrent + 1].CourierType.PalletsPerTrailer)
{
courierTypeRegion.CurrentAllocation = remaining;
courierTypeRegionOutput.Add(courierTypeRegion);
processed = true;
}
}
}
courierCurrent++;
}
if (!processed)
{
if (courierTypeRegions.Count > 0)
{
var courierTypeRegionRemaining =
courierTypeRegions.Where(x => x.CourierType.PalletsPerTrailer >= remaining).OrderByDescending(
x => x.CourierType.PalletsPerTrailer).SingleOrDefault();
if (courierTypeRegionRemaining != null) courierTypeRegionOutput.Add(courierTypeRegionRemaining);
processed = true;
}
}
var currentRemaining = quantity - courierTypeRegionOutput.Sum(x => x.CourierType.PalletsPerTrailer);
if (currentRemaining > 0)
{
FindClosest(quantity, currentRemaining, courierTypeRegions, courierTypeRegionOutput);
}
return courierTypeRegionOutput;
}

'CourierTypeRegion' is one instance that lives for the entire foreach loop, it is not instantiated and destroyed every loop iteration. You are repeatedly adding the same instance to your list. You end up with a list where all items reference the the last value in the loop.
You need to change your foreach loop as follows:
foreach (var courierTypeRegion in courierOrderByDesc)
{
var courierRegionCopy = courierTypeRegion;
if (remaining >= courierTypeRegion.CourierType.PalletsPerTrailer && !processed)
{
courierRegionCopy.CurrentAllocation = courierTypeRegion.CourierType.PalletsPerTrailer;
courierTypeRegionOutput.Add(courierRegionCopy);
processed = true;
}
if (!processed)
{
if (courierOrderByDesc[courierCurrent + 1] != null)
{
if (remaining > courierOrderByDesc[courierCurrent + 1].CourierType.PalletsPerTrailer)
{
courierRegionCopy.CurrentAllocation = remaining;
courierTypeRegionOutput.Add(courierRegionCopy);
processed = true;
}
}
}
courierCurrent++;
}

Related

Return two collections in one

how can i add collection to another collection? I am returnging one collection _lastOpenedArticles and if that collection has less than 3 articles i need to add articles from allUsersArticles. However i keep getting article from collection _lastOpenedArticles 3 times. Can you help please.
async Task ShowLastListenedAsync(List<Article> allUserArticles)
{
var downloadedArticles = LangUpDataSaverLoader.DeserializeAllOptimizationData();
if (_lastOpenedArticles != null && _lastOpenedArticles.Count > 0)
{
foreach (var article in _lastOpenedArticles.Take(3))
{
var filename = string.Format(SharedConstants.ArticleImageUrl, SharedConstants.ApiBaseUri, article.Id);
var newCell = new ArticleDetailData()
{
Author = article.Author,
Id = article.Id,
};
if (downloadedArticles.DownloadedArticles.Any(m => m.Id == article.Id))
{
newCell.BackgroundImage = article.Id.ArticleImageFile();
}
else
{
newCell.BackgroundImage = filename;
}
var sec = article.Category;
if (sec == null)
{
newCell.Section = " ";
}
else
{
newCell.Section = article.Category;
}
LastThreeArticles.Add(newCell);
if (_lastOpenedArticles.Count < 3)
{
foreach (var art in allUserArticles.Take(3))
{
filename = string.Format(SharedConstants.ArticleImageUrl, SharedConstants.ApiBaseUri, article.Id);
var cell = new ArticleDetailData()
{
Author = article.Author,
BackgroundImage = filename,
Id = article.Id,
};
sec = article.Category;
if (sec == null)
{
cell.Section = " ";
}
else
{
cell.Section = article.Category;
}
LastThreeArticles.Add(cell);
}
await FillAnonymousArticles(allUserArticles);
}
}
}
else
{
await FillAnonymousArticles(allUserArticles);
}
}
Instead of checking the count in the for loop, I'd propose to move the check whether you already have 3 articles after the loop. So you avoid to get the articles 3 times (once for each run of the loop):
async Task ShowLastListenedAsync(List<Article> allUserArticles)
{
var downloadedArticles = LangUpDataSaverLoader.DeserializeAllOptimizationData();
if (_lastOpenedArticles != null && _lastOpenedArticles.Count > 0)
{
foreach (var article in _lastOpenedArticles.Take(3))
{
var filename = string.Format(SharedConstants.ArticleImageUrl, SharedConstants.ApiBaseUri, article.Id);
var newCell = new ArticleDetailData()
{
Author = article.Author,
Id = article.Id,
};
if (downloadedArticles.DownloadedArticles.Any(m => m.Id == article.Id))
{
newCell.BackgroundImage = article.Id.ArticleImageFile();
}
else
{
newCell.BackgroundImage = filename;
}
var sec = article.Category;
if (sec == null)
{
newCell.Section = " ";
}
else
{
newCell.Section = article.Category;
}
LastThreeArticles.Add(newCell);
}
// Move this check out of the for loop
if (_lastOpenedArticles.Count < 3)
{
foreach (var art in allUserArticles.Take(3))
{
filename = string.Format(SharedConstants.ArticleImageUrl, SharedConstants.ApiBaseUri, article.Id);
var cell = new ArticleDetailData()
{
Author = article.Author,
BackgroundImage = filename,
Id = article.Id,
};
sec = article.Category;
if (sec == null)
{
cell.Section = " ";
}
else
{
cell.Section = article.Category;
}
LastThreeArticles.Add(cell);
}
await FillAnonymousArticles(allUserArticles);
}
}
else
{
await FillAnonymousArticles(allUserArticles);
}
}
In addition, you could use Linq to create a union of the lists. This will result in much shorter code, for example:
_lastOpenedArticles
.Union(allUserArticles)
.Take(3)
.Select(x => ConvertToCell(x)) // You need this method that converts the articles to a cell
.ToArray(); // If you need a list, you can also use ToList()
_lastOpenedArticles.AddRange(allUserArticles)
_lastOpenedArticles.Concat(allUserArticles)
the last one returns IEnumerable<T> and first one is void. Take one that suits you better

ICollectionView multiple filter

So I have a fitler TextBox where I want to search for different type of docs in a grid the code where I have different type of columns such as: Date,DocId,ClientId.
For a search I write on a filter Textbox something like that DocId:2002 and It just work fine but when I try to make a multiple search for example DocId:2002 ClientId:201 It doesnt search because of the return it just does an infinite loop.
private void TextBoxFilter_TextChanged(object sender, TextChangedEventArgs e)
{
foreach (Match m in Regex.Matches((sender as TextBox).Text, pattern, options))
{
if (m.Value != "")
{
Func<String, String> untilSlash = (s) => { return filters[re.Match(s).Groups[1].ToString()] = re.Match(s).Groups[2].ToString(); };
untilSlash(m.Value);
}
}
ICollectionView cv = CollectionViewSource.GetDefaultView(this.DataGridDocList.ItemsSource);
if (filters.Count == 0)
{
cv.Filter = null;
}
else
{
cv.Filter = o =>
{
for (int i = 0; i < filters.Count; i++)
{
if (filters.ElementAt(i).Key == "Date")
{
if (DateVerify.Match(filters.ElementAt(i).Value).Success)
{
return (o as Document).DateCreated < Convert.ToDateTime(DateVerify.Match(filters.ElementAt(i).Value).Groups[1].ToString()) && (o as Document).DateCreated > Convert.ToDateTime(DateVerify.Match(filters.ElementAt(i).Value).Groups[2].ToString());
}
else
{
var dateString = (o as Document).DateCreated.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture);
return dateString.Contains(DateVerify.Match(filters.ElementAt(i).Value).Groups[1].ToString());
}
}
if (filters.ElementAt(i).Key == "DocId")
{
return (o as Document).DocumentId.ToString().Contains(filters.ElementAt(i).Value);
}
if (filters.ElementAt(i).Key == "ClientId")
{
return (o as Document).ClientId.ToUpper().Contains(filters.ElementAt(i).Value.ToUpper());
}
}
return false;
};
filters.Clear();
}
}
So my question is how can I do an big search with all the filters at one time?
Manually I can add them 1 by 1 and it will be something like search1 && search2 && search3 but It will take too much time and It's probably not the best solution
There are many ways of building up the predicate. However my suggestion is to keep it simple and just create one method that returns true or false. It's good practice to only return once in a method.
The code below if for illustration purposes (as I'm unable to test it):
ICollectionView cv = CollectionViewSource.GetDefaultView(this.DataGridDocList.ItemsSource);
if (filters.Any())
{
cv.Filter = new Predicate<object>(PredicateFilter);
}
else
{
cv.Filter = null;
}
Then Predicate method to filter results:
public bool PredicateFilter(object docObj)
{
Document doc = docObj as Document;
var response = new List<bool>();
for (int i = 0; i < filters.Count; i++)
{
if (filters.ElementAt(i).Key == "Date")
{
if (DateVerify.Match(filters.ElementAt(i).Value).Success)
{
response.Add(doc.DateCreated < Convert.ToDateTime(DateVerify.Match(filters.ElementAt(i).Value).Groups[1].ToString()) && doc.DateCreated > Convert.ToDateTime(DateVerify.Match(filters.ElementAt(i).Value).Groups[2].ToString()));
}
else
{
var dateString = doc.DateCreated.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture);
response.Add(dateString.Contains(DateVerify.Match(filters.ElementAt(i).Value).Groups[1].ToString()));
}
}
else if (filters.ElementAt(i).Key == "DocId")
{
response.Add(doc.DocumentId.ToString().Contains(filters.ElementAt(i).Value));
}
else if (filters.ElementAt(i).Key == "ClientId")
{
response.Add(doc.ClientId.ToUpper().Contains(filters.ElementAt(i).Value.ToUpper()));
}
}
return response.All(m => m); // if all filters came back with true, return 1 response of true else false.
}

Collection was modified; enumeration operation may not execute. When the jobject is modified

This was working fine earlier but not sure after I updated the newtonsoft version this started breaking .
This is the error that I am getting : Collection was modified; enumeration operation may not execute.
JObject jObjectFull = (JObject)dynObjFullJArray[index];
JArray jArrayFull = (JArray)jObjectFull[packageName];
int fullCount = jObjectFull[packageName].Count();
if (packageName == "hello")
{
if (ishello)
{
ishellochck= false;
jArrayFull.RemoveAll();
}
jArrayFull.Add(JObject.FromObject(dynObjItem));
}
else
{
foreach (var item in jObjectFull[packageName])
{
int itemId = (int)item.SelectToken(selectedIdOrderNo.Split('|')[0]);
if (dynObjItemId == itemId)
{
//Edit
flag = true;
jArrayFull[i].Remove();
if (i == 0)
{
if (fullCount > 1)
jArrayFull[i].AddBeforeSelf(JObject.FromObject(dynObjItem));
else
jArrayFull.Add(JObject.FromObject(dynObjItem));
}
else
{
if (i == (fullCount - 1))
{
--i;
jArrayFull[i].AddAfterSelf(JObject.FromObject(dynObjItem));
}
else
jArrayFull[i].AddBeforeSelf(JObject.FromObject(dynObjItem));
}
}
i++;
}`
You don't say what versions of Json.NET you were and are using, however it is correct that the exception is thrown, because you are modifying the collection through which you are iterating:
jArrayFull is defined to be jObjectFull[packageName],
So when you do jArrayFull[i].Remove() you are actually doing ((JArray)jObjectFull[packageName]).RemoveAt(i), thereby modifying the collection.
JArray implements IList<JToken>, so perhaps you just want to do:
JObject jObjectFull = (JObject)dynObjFullJArray[index];
JArray jArrayFull = (JArray)jObjectFull[packageName];
if (packageName == "hello")
{
if (ishello)
{
ishellochck = false;
jArrayFull.RemoveAll();
}
jArrayFull.Add(JObject.FromObject(dynObjItem));
}
else
{
for (int i = 0, fullCount = jArrayFull.Count; i < fullCount; i++)
{
int itemId = (int)jArrayFull[i].SelectToken(selectedIdOrderNo.Split('|')[0]);
if (dynObjItemId == itemId)
{
//Edit
flag = true;
jArrayFull[i] = JObject.FromObject(dynObjItem);
// Maybe break here?
}
}
}

Jump out from Inner-Foreach to Outer-Foreach, Keep Inner-Foreach where you left it

I have the following code:
foreach (XNode rowXml in elements.Nodes())
{
foreach (DataRow rowDB in dataSetDB.Tables[0].Rows)
{
string itemDB = rowDB[0].ToString().ToUpper();
string itemXml = rowXml.ToString().ToUpper();
if (itemDB == itemXml)
{
//If itemDB == itemXml; jump to Outer_Foreach
}
if (itemDB != itemXml)
{
//If itemDB != itemXml; jump to Outer_Foreach
}
}
How is it possible to get out of Inner-Foreach and up to Outer-Foreach, and still keep both foreach where you left it. I am looping through a DB and XML table rows. Break; completley jumps out of the Inner-Foreach and im not able to catch where I left it, so I start back on index[0], when I loop through it over and over again.
It sounds like you need 'for' loop.
int i = 0;
int k = 0;
bool shouldBreak;
var nodes = elements.Nodes();
var rows = dataSetDB.Tables[0].Rows;
for (i = 0; i < nodes.Count(); i++)
{
for(k = 0; k < rows.Count(); k++)
{
string itemDB = rows[k][0].ToString().ToUpper();
string itemXml = nodes[i].ToString().ToUpper();
if (itemDB == itemXml)
{
shouldBreak = true;
break;
}
if (itemDB != itemXml)
{
shouldBreak = true;
break;
}
}
if (toBreak)
break;
}
Now if you'll break the inner loop can know where it broke by accessing i and k
This answer is stolen from this answer here. What you want to achieve is a zip operation. For more information see the answer that I linked.
var xmlNodes = elements.Nodes();
var dbNodes = dataSetDB.Tables[0].Rows;
var xmlAndDB = xmlNodes.Zip(dbNodes , (x, d) => new { xNode = x, dNode = d });
foreach(var xd in xmlAndDB )
{
Console.WriteLine(xd.xNode + xd.dNode);
string itemDB = xd.dNode[0].ToString().ToUpper();
string itemXml = xd.xNode.ToString().ToUpper();
if (itemDB == itemXml)
{
//If itemDB == itemXml;
}
else /* if (itemDB != itemXml) */
{
//If itemDB != itemXml;
}
}

optimising linq queries to incres

I have the the below linq queries searching a the same data table and was wondering if it would be possible to make one search and do the below for loops to add data to to the same variables so that it can make the system faster.
var sort = configurationData.AsEnumerable().Where(sorts => sorts.Field<String>("QuestionStartText") == question &&
sorts.Field<String>("slideNo") == Convert.ToString(slideNumber) )
.Select(sorted => sorted.Field<String>("SortByColumn")).Distinct().AsParallel();
var rowNeedAfterSort = configurationData.AsEnumerable().Where(sorts => sorts.Field<String>("QuestionStartText") == question &&
sorts.Field<String>("slideNo") == Convert.ToString(slideNumber))
.Select(sorted => sorted.Field<String>("NoOfRows")).Distinct().AsParallel();
var indexs = configurationData.AsEnumerable().Where(sorts => sorts.Field<String>("QuestionStartText") == question &&
sorts.Field<String>("slideNo") == Convert.ToString(slideNumber))
.Select(sorted => sorted.Field<String>("ColumnInExcel")).Distinct().AsParallel();
int p = 0;
int chartValue = 0;
foreach (string inedcies in indexs)
{
if (inedcies != null)
{
if (!inedcies.ToUpper().Equals("NULL"))
{
if (inedcies.Contains(','))
{
Array.Clear(valuesUsed, 0, valuesUsed.Length);
string[] index = inedcies.Split(',');
foreach (string a in index)
{
valuesUsed[p] = Convert.ToInt32(a);
p++;
}
}
else if (inedcies.Equals("7"))
{
Array.Clear(valuesUsed, 0, valuesUsed.Length);
valuesUsed[p] = Convert.ToInt32(inedcies);
}
else
{
chartValue = Convert.ToInt32(inedcies);
}
}
}
}
foreach (string sortedint in sort)
{
if (sortedint != null)
{
if (!sortedint.ToUpper().Equals("NULL"))
{
SortData2(sortedint);
sortedData = "true";
}
}
}
foreach (string rows in rowNeedAfterSort)
{
if (rows != null)
{
if (!rows.ToUpper().Equals("NULL"))
{
string[] values = rows.Split(' ');
rowCount = Convert.ToInt32(values[1]);
}
}
}
Once you clean up the below codesmell you will be able to see how/where you can take out the foreach loops.
foreach (string a in index)
{
valuesUsed[p] = Convert.ToInt32(a);
p++;
}
what assurances do we have that index.length < valuesUsed.length ?
Foreach (x in y)
{
if (x != null)
{
do something
}
}
More readable. You also do not need to check if an element is null in a foreach loop. The properties of the element may need to be checked, but not the element itself.
y.Foreach(x => do what you need here)

Categories