Passing data out - c#

I'm currently having a bit of a nightmare with a foreach loop. In a nutshell, what I am trying to do is split a string and then filter this data based on the string. I then need to bind the said data to a control of filter it further down the line. So far, I have the following code
if (Session["Contract"] != null)
{
string[] contract = Session["Contract"].ToString().Split(',');
foreach (string i in contract)
{
if (i.ToString() != "")
{
data = data.Where(x => x.Term.Trim().ToUpper().Contains(i.ToString().Trim().ToUpper()));
}
}
}
LV_Jobs.DataSource = data;
LV_Jobs.DataBind();
Now when looping through, the filtering works fine, but once you are finished with one item, the data variable is cleared? Obviously I need to pass "data" back out of the foreach loop. Can anyone point me in the direction of how to do this???

You are resetting data every time the loop iterates. Try this (depending on what data is)
var filteredData = new List<string>();
if (Session["Contract"] != null)
{
filteredData = Session["Contract"].ToString().Split(',').Join(
data,
i => i.ToString().Trim().ToUpper(),
x => x.Term.Trim().ToUpper(),
(i, x) => x);
}
LV_Jobs.DataSource = filteredData;
LV_Jobs.DataBind();

Simply collect the needed data out in a list of results or any other data structure declared outside of the for/foreach scope.

Your data variable is not cleared. Instead, in the last iteration of the foreach where i.ToString() != "" your Where() condition is not true. So data becomes an empty list.
You can break out of the foreach when you found what you were looking for.

Related

Delete the current element in an array

How can I delete the current element of an array inside a foreach-loop?
My program gets data form a DB and sends it to a new one via HTTP requests. Now I want to post a JSON string to my new DB. If it was a success I want to delete the current array item which I'm working with. Something like this.
foreach(var item in array)
{
bool decide = method.DoSomething();
if(decide == true)
{
//delete current item
}
}
since you cannot delete items from an array and change the size of it here is a loop approach using a second collection
List<itemClass> keepCollection = new List<itemClass>();
foreach(var item in array)
{
bool decide = method.DoSomething();
if(decide == false)
{
keepCollection.Add(item);
}
}
If you need it again in array form just call ToArray()
var finalResult = keepCollection.ToArray();
appraoch with Linq which creates a new array with valid elements and overwrites the existing array
array = array.Where(x => !method.DoSomething(x)).ToArray(); //select valid elements
there are 2 ways (both were tested)
foreach (var item in array.ToList())
{
bool decide = method.DoSomething();
if (decide == true)
{
item.Remove();
}
}
and
for ( i=0; i < array.Length; i++)
{
bool decide = method.DoSomething();
if (decide == true)
{
array[i].Remove();
}
}
Whenever you want to delete entries from a collection, you should never loop through that collection from beginning to end, but always from end back to beginning.
By the way, C# does not allow you deleting entries from a collection while looping through that collection using a foreach loop.

Alternative to setting values in foreach C#

I'm rather new to MVC/C# and from what I understand, foreach is read-only.
I would like there to be a method that sets the values that are null to false if that method is called.
IQueryable<CurrentMatch> FindTheMatch = (from row in db.CurrentMatches
where row.UserId.ToString() == UserIdentity
where row.MatchID == MatchIdentity
select row);
List<CurrentMatch> SetRemainingValuesToFalse = FindTheMatch.ToList();
I know that the part below wont work, it just demonstrates how I'm trying to achieve what I want to do.
foreach (var Column in SetRemainingValuesToFalse)
{
if (Column == null)
{
Column = false;
}
}
As the row has a large number of properties it wouldn't be scaleable in the future to set each property manually.
You just need to use a standard for loop instead of a foreach. You can't modify the collection inside a foreach because that is how the iterator works. You can however modify values on the objects themselves.
See also: Changing objects value in foreach loop?
I think you have this sort of the wrong way round. If you set that value to false inside any sort of loop, the context is lost when you exit that iteration of the loop.
Instead, what you probably want to do is, when consuming the list, treat nulls as false. You can use the null coalesce operator for this (??)
foreach (var row in FindTheMatch)
{
DoSomethingInterestingWith(row.Column ?? false); // pass false if Column is null.
}
for(int i=0;i<SetRemainingValuesToFalse.length;i++)
{
if (SetRemainingValuesToFalse[i] == null)
{
SetRemainingValuesToFalse[i] = false;
}
}
you are slightly misunderstanding how the foreach is working
foreach(var c in col)
reads as
While col.asEnumerable.HasValues let c = col.asEnumerable.Current
because of this you can't change either the enumerable or its current value with out breaking the loop, however if the enumerable isn't attached to the collection you are changing then you have no problems
ToList for example will clone the collection meaning the enumerable is attached to the clone not the original collection
foreach(var c in col)
col.Remove(c);
will error
foreach(var c in col.ToList())
col.Remove(c);
works fine
like wise
foreach(var c in col)
if(c.Field == null) c.Field = false;
is also fine because you are editing the the content of the current enumerable location not the location itself
however your stated desire of just replacing nulls in a collection is much simpler
col.Select(c=>c??false); //c#6
col.Select(c=>c == null? false : c); //c#<6
as you seem to be working with something akin to a datatable then you could do this
foreach(var row in table.Rows)
foreach(var col in table.Columns)
row[col] = row[col] ?? false;

Linq OrderBy not sorting correctly 100% of the time

I'm using the Linq OrderBy() function to sort a generic list of Sitecore items by display name, then build a string of pipe-delimited guids, which is then inserted into a Sitecore field. The display name is a model number of a product, generally around 10 digits. At first it seemed like this worked 100% of the time, but the client found a problem with it...
This is one example that we have found so far. The code somehow thinks IC-30R-LH comes after IC-30RID-LH, but the opposite should be true.
I put this into an online alphabetizer like this one and it was able to get it right...
I did try adding StringComparer.InvariantCultureIgnoreCase as a second parameter to the OrderBy() but it did not help.
Here's the code... Let me know if you have any ideas. Note that I am not running this OrderBy() call inside of a loop, at any scope.
private string GetAlphabetizedGuidString(Item i, Field f)
{
List<Item> items = new List<Item>();
StringBuilder scGuidBuilder = new StringBuilder();
if (i != null && f != null)
{
foreach (ID guid in ((MultilistField)f).TargetIDs)
{
Item target = Sitecore.Data.Database.GetDatabase("master").Items.GetItem(guid);
if (target != null && !string.IsNullOrEmpty(target.DisplayName)) items.Add(target);
}
// Sort it by item name.
items = items.OrderBy(o => o.DisplayName, StringComparer.InvariantCultureIgnoreCase).ToList();
// Build a string of pipe-delimited guids.
foreach (Item item in items)
{
scGuidBuilder.Append(item.ID);
scGuidBuilder.Append("|");
}
// Return string which is a list of guids.
return scGuidBuilder.ToString().TrimEnd('|');
}
return string.Empty;
}
I was able to reproduce your problem with the following code:
var strings = new string[] { "IC-30RID-LH", "IC-30RID-RH", "IC-30R-LH", "IC-30R-RH"};
var sorted = strings.OrderBy(s => s);
I was also able to get the desired sort order by adding a comparer to the sort.
var sorted = strings.OrderBy(s => s, StringComparer.OrdinalIgnoreCase);
That forces a character-by-character (technically byte-by-byte) comparison of the two strings, which puts the '-' (45) before the 'I' (73).

Hitting the database in loop

I am hitting the database in loop. Pls suggest how i can avoid this ?
foreach (var assignedPricing in reviewingPricing)
{
var assignedUserId = _wfActivitySvc.GetPricingReviewer(assignedPricing.PricingID).UserId;
if (assignedUserId == UserId)
{
reviewingAssignedPricings.Add(assignedPricing);
}
}
Create a new query in your database service:
//Build an collection with just unique ids
var priceIds = reviewingPricing.Select(x => x.PricingId).Distinct();
//Return a key/value collection with all priceId/UserId
var reviewerMap = _wfActivitySvc.GetAllReviewersByPriceId(priceIds);
//now you can loop without db queries
foreach (var pricing in reviewingPricing)
{
var reviewer = reviewMap.FirstOrDefault(x => x.PricingId == pricing.PricingId);
if (reviewerMap == null)
continue;
if (reviewer.UserId == UserId)
{
reviewingAssignedPricings.Add(pricing);
}
}
1) You may want to insert all records at once. You can create stored procedure to do this. If you use SQL server you can use BulkInserter class: https://github.com/ronnieoverby/RonnieOverbyGrabBag/blob/master/BulkInserter.cs
For production I had to tweak it a little bit internally to reduce its initialization time, but for infrequent bulk inserts Github version is just fine.
Usage example:
var bulkInserter = new BulkInserter<YourClass>(SqlConnection, "Table Name");
bulkInserter.Insert(reviewingAssignedPricings);
bulkInserter.Flush();
2) If this reads from database every time
_wfActivitySvc.GetPricingReviewer(assignedPricing.PricingID).UserId;
then replace it with single call outside loop to get all reviweres from database, then add to dictionary (key = priceID, value = reviewer) and then get reviewers from dictionary within loop. If you use simple List and .FirstOrDefault(), then it can be noticeably slow for 100+ items in list. jgauffin answer describes this idea.

Update List element at specified list item position

I am trying to do this:
foreach (Settings sets in MySets)
{
if (sets.pName == item.SubItems[2].Text)
{
var ss = new SettingsForm(sets);
if (ss.ShowDialog() == DialogResult.OK)
{
if (ss.ResultSave)
{
sets = ss.getSettings();
}
}
return;
}
}
But since the sets spawned variable is readonly, I cant override it.
I would also like to do something like this
foreach (Settings sets in MySets)
{
if(sets.pName == someName)
sets.RemoveFromList();
}
How can I accomplish this? Lists have a very nice Add() method, but they forgot the rest :(
You can use:
MySets.RemoveAll(sets => sets.pName == someName);
to remove all the items that satisfy a specific condition.
If you want to grab all the items satisfying a condition without touching the original list, you can try:
List<Settings> selectedItems = MySets.FindAll(sets => sets.pName == someName);
foreach loops don't work here as trying to change the underlying list will cause an exception in the next iteration of the loop. Of course, you can use a for loop and manually index the list. However, you should be very careful not to miss any items in the process of removing an item from the list (since the index of all the following items will get decremented if an element is removed):
for (int i = 0; i < MySets.Count; ++i) {
var sets = MySets[i]; // simulate `foreach` current variable
// The rest of the code will be pretty much unchanged.
// Now, you can set `MySets[i]` to a new object if you wish so:
// MySets[i] = new Settings();
//
// If you need to remove the item from a list and need to continue processing
// the next item: (decrementing the index var is important here)
// MySets.RemoveAt(i--);
// continue;
if (sets.pName == item.SubItems[2].Text)
{
var ss = new SettingsForm(sets);
if (ss.ShowDialog() == DialogResult.OK)
{
if (ss.ResultSave)
{
// Assigning to `sets` is not useful. Directly modify the list:
MySets[i] = ss.getSettings();
}
}
return;
}
}
You can't do it in a 'regular' for loop?

Categories